Understanding Unity player movement 2d - c#

I know how to do Unity 3d movmement and its very simple but when I started to do 2d movement I got very confused on what the code actual means Im new to Unity and C# and I would like to understand what this code means
if (horizontalInput > 0.01f)
transform.localScale = Vector3.one;
else if (horizontalInput > -0.01f)
transform.localScale = new Vector3(-1, 1, 1);
if (Input.GetKey(KeyCode.Space))
body.velocity = new Vector2(body.velocity.x, speed);

This code applies transformation to the entity. There's no much context but I'll try my best as I've worked with Unity.
if (horizontalInput > 0.01f)
transform.localScale = Vector3.one;
else if (horizontalInput > -0.01f)
transform.localScale = new Vector3(-1, 1, 1);
What the whole structure is doing here is applying a scale to the entity base on the value of horizontalInput. In the if, you scale positive to the local X axis. In the else if, you scale negative to the local X axis You can get the documentation of localScale here Transform.localScale
Vector is a class and one is a static property that represent the unitary vector (1, 1, 1). It's a shorthand useful so you don't have to write new Vector3(1, 1, 1)
if (Input.GetKey(KeyCode.Space))
body.velocity = new Vector2(body.velocity.x, speed);
This block here uses Input to check for a user event. In this case, if the user pressed the spacebar. Input class is used to get information from your keyboard, mouse, etc. Keycode is a class that lists several key codes (as you might know, every key is assigned a code that can help to identify the key in case of a user event). See Input.
If user is pressing the spacebar, it applies a Vector2 to body.velocity to move your entity. Of course, it is not written but I'm guessing that body is a RigidBody2D instance because velocity is a property of such class used to move it. See Rigidbody2D.velocity
You may be wondering why it uses Vector3 in a 2D context. Both Vector2 and Vector3 represent vectors, but Transform.localScale requires a Vector3 to work as this property is applicable to both 2D and 3D context. Rigidbody2D.velocity on the other hand is only 2D (X and Y axis) as the class name implies.

Related

Using MoveRotation in Unity 3D to turn player towards a certain angle

I've been told that Rigidbody.MoveRotation is the best way in Unity 3D to rotate the player between fixed positions while still detecting hits. However, while I can move smoothly from fixed position to position with:
if (Vector3.Distance(player.position, targetPos) > 0.0455f) //FIXES JITTER
{
var direction = targetPos - rb.transform.position;
rb.MovePosition(transform.position + direction.normalized * playerSpeed * Time.fixedDeltaTime);
}
I can't find out how to rotate smoothly between fixed positions. I can rotate to the angle I want instantly using Rigidbody.MoveRotation(Vector3 target);, but I can't seem to find a way to do the above as a rotation.
Note: Vector3.Distance is the only thing stopping jitter. Has anyone got any ideas?
First of all MoveRotation doesn't take a Vector3 but rather a Quaternion.
Then in general your jitter might come from overshooting - you might be moving further than the distance between your player and target actually is.
You can avoid that bit by using Vector3.MoveTowards which prevents any overshooting of the target position like e.g.
Rigidbody rb;
float playerSpeed;
Vector3 targetPos;
// in general ONLY g through the Rigidbody as soon as dealing wit Physics
// do NOT go through transform at all
var currentPosition = rb.position;
// This moves with linear speed towards the target WITHOUT overshooting
// Note: It is recommended to always use "Time.deltaTime". It is correct also during "FixedUpdate"
var newPosition = Vector3.MoveTowards(currentPosition, targetPos, playerSpeed * Time.deltaTime);
rb.MovePosition(newPosition);
// [optionally]
// Note: Vector3 == Vector3 uses approximation with a precision of 1e-5
if(rb.position == targetPos)
{
Debug.Log("Arrived at target!");
}
Then you can simply apply this same concept also to rotation by going through the equivalent Quaternion.RotateTowards basically just the same approach
Rigidbody rb;
float anglePerSecond;
Quaternion targetRotation;
var currentRotation = rb.rotation;
var newRotation = Quaternion.RotateTowards(currentRotation, targetRotation, anglePerSecond * Time.deltaTime);
rb.MoveRotation(newRotation);
// [optionally]
// tests whether dot product is close to 1
if(rb.rotation == targetRotation)
{
Debug.Log("Arrived at rotation!");
}
You can go one step further and use a tweeting library to tween between rotations.
DOTween
With that you can call it like this:
rigidbody.DoRotate(target, 1f) to rotate to target in 1 second.
Or even add callbacks.
rigidbody.DoRotate(target, 1f).OnComplete(//any method or lambda you want)
If at some point you want to cancel the tween yuou can save it on a variable and then call tween.Kill();
So, you want to animate the rotation value over time until it reaches a certain value.
Inside the Update method, you can use the Lerp method to keep rotating the object to a point, but you will never really reach this point if you use Lerp. It will keep rotating forever (always closer to the point).
You can use the following:
private bool rotating = true;
public void Update()
{
if (rotating)
{
Vector3 to = new Vector3(20, 20, 20);
if (Vector3.Distance(transform.eulerAngles, to) > 0.01f)
{
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles, to, Time.deltaTime);
}
else
{
transform.eulerAngles = to;
rotating = false;
}
}
}
So, if the distance between the current object angle and the desired angle is greater than 0.01f, it jumps right to the desired position and stop executing the Lerp method.

How to make LookAt align the transform.up vector instead of transform.forward?

The problem is that i cannot make a script that makes the enemy rotate so that "up" is pointing against the player, (with that i mean so that it will come to the player with transform.up) And i have not found any working solution yet, (from what i can tell)
I am trying to make a small 2D game, just for my friends to play. What i have tried is making the enemy look at the player and then set the "z" rotation to the same as the "x" rotation and after that reset the "x" and "y" rotation (so that "up" is pointing at the player). But the enemy just goes straight up.
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform Player; // The Transform of the Player that you can controll
public float MoveSpeed = 4; // How fast the enemy should go
void FixedUpdate()
{
transform.LookAt(Player);
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
transform.position += transform.up * MoveSpeed * Time.deltaTime;
}
}
So what i want to happen is that the Enemy will move to the Player but the rotation part is not working and it just goes straight up in the air instead of following the player. I might just be stupid and have missed something simple...
By using
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
both times you overwrite the rotation with new values additionally mixing Euler (Vector3) with Quaternion fields ... transform.rotation.x is not the pure rotation around the X-axis you see in the Unity inspector.
It depends a bit on how exactly you want the enemy rotated in the end but you could simply use Transform.RotateAround like
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
LookAt by default results in a rotation such that transform.forward is pointing at the target, transform.right remains pointing right(ish) and transform.up remains pointing up(ish). So all you have to do is rotating around that local X-axis transform.right exactly 90° so now transform.forward points down instead and transform.up points at the target.
which results in
If you than also want to change the other axis you can still additionally rotate it around the local transform.up axis as well e.g. to flip the forward vector "upwards":
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
transform.RotateAround(transform.position, transform.up, 180);
Is there actually a special reason why you are doing that in FixedUpdate and not rather in Update? FixedUpdate should only be used for Physics related stuff.

Unity3D - Make character moving forward nicely on a seesaw in a 2.5D runner game

What I want to do is to make a kind of 2.5D runner game in Unity, which the character's all three rotation axises are frozen and the position on Z axis is also frozen. I don't know how to make the character moving forward nicely on the seesaw. (I create the seesaw by using HingeJoint.)
I create a struct to detect the CapsuleCollider status by using Physics.Raycast() function and that works fine.
private struct ColliderStatus
{
public bool headed; //colliding up
public bool footed; //colliding down
public bool onPlane; //colliding down && the obstacle colliding does not have slope angle
public bool lefted; //colliding left
public bool righted; //colliding right
public bool inAir; //not colliding anything
}
I've tried these ways:
Add force on Rigidbody to move forward
//To move character rigidbody move forward automatically in runner game
//when the speed is lower than the minimum speed and it's on plane or in air.
if (rigidbody.velocity.x < minForwardSpeed && (colliderStatus.onPlane || colliderStatus.inAir))
{
rigidbody.AddForce(20f * Vector3.right);
}
//Add gravity to player
Vector3 gravityForce = new Vector3(0f, -gravityOnPlayer, 0f);
rigidbody.AddForce(gravityForce);
It doesn't work well because the character continue going up when it's on the seesaw though the seesaw starts to tilt. And there will be a velocity loss when the character fall to ground from a higher plane or after jumping and what it looks like is that the character will stunned for a little moment on the landing point and then begin to accelerate.
Use transform.Translate() to move forward && change the way of adding gravity
//Use transform.Translate() to move forward
//I recognize that by this way, there will be no velocity loss
//when the character falling down to the ground at the landing point
//If I don't use this condition, my character will stuck on the
//right vertical wall
if (!colliderStatus.righted)
{
transform.Translate(new Vector2(minForwardSpeed, 0f) * Time.deltaTime);
}
I don't know why I can't write like this since it will cause the velocity doesn't react correctly:
//Use transform.Translate() to move forward
if (!colliderStatus.righted && rigidbody.velocity.x < minForwardSpeed)
{
transform.Translate(new Vector2(minForwardSpeed, 0f) * Time.deltaTime);
}
To change the way of adding gravity, I use a function SlopeAngleVector() to calculate the slope vector the character is running on.
private Vector3 SlopeAngleVector()
{
Vector3 nextStepPositon = new Vector3(transform.position.x + 0.01f, transform.position.y, 0f);
Ray nextPosRay = new Ray(nextStepPositon, Vector3.down);
Ray nowPosRay = new Ray(transform.position, Vector3.down);
RaycastHit nextPosHit;
RaycastHit nowPosHit;
Vector3 slopeAngle = Vector3.zero;
Physics.Raycast(nowPosRay, out nowPosHit, 5f, obstaclesLayerMask);
if (Physics.Raycast(nextPosRay, out nextPosHit, 5f, obstaclesLayerMask))
{
slopeAngle = new Vector3(nextPosHit.point.x - nowPosHit.point.x, nextPosHit.point.y - nowPosHit.point.y, 0f).normalized;
}
return slopeAngle;
}
Then I add the gravity by calculate the gravity projection on the slope vector:
private void AddGravity()
{
Vector3 gravityForce = new Vector3(0f, -gravityOnPlayer, 0f);
//my character could be collided by the long vertical wall(colliderStatus.righted)
//so I set the condition as "!colliderStatus.footed"
//otherwise, I would use "colliderStatus.inAir"
if (!colliderStatus.footed)
{
gravityForce = new Vector3(0f, -gravityOnPlayer, 0f);
}
else
{
gravityForce = Vector3.Project(Vector3.down * gravityOnPlayer, SlopeAngleVector());
}
rigidbody.AddForce(gravityForce);
}
Now my character can slide down from the seesaw but it will keep going backwards. And it cannot make it through when on the low slope angle seesaw.
How to make a good behavior script for the runner on seesaw?
I'd suggest looking at some of the Unity standard asset character controllers, I believe they take slopes into account for their character movement. It may give you some ideas.
I'd also recommend modifying the way your code calculates the angle of the slope. The raycast hit will give you back a surface normal, you should then be able to use the Vector3.Cross to figure out the angle of the slope.
It'll be something like: Vector3.Cross(normal, (vector that points away from screen)).
You may need to tweak it to get it working correctly but this can give you the slope angle in one raycast. It may also eliminate potential issues of your move to position being just below the see saw.
As a general tip, try not to mix transform and rigidbody stuff together, if you want to move the rigidbody, move the rigidbody directly, not indirectly through the transform.

Make time measuring independent of FPS (2D C#)

Lets say I'm working in a 2D plane, with a person in the bottom of my screen, and an object that moves down along the Y-axis.
The moving object has following movement code:
transform.position += new Vector3 (0, -1, 0) * speed * Time.deltaTime;
Now, as I want to be able to modify how long it takes for the object to connect with the player, I created this, where spawnTimeDistance is our key factor:
Instantiate (obstacle, player.transform.position + new Vector3(-0.50F , (spawnTimeDistance + obstacleDimensionY + pScript.playerDimensionY), -1), Quaternion.identity);
As can be seen, spawnTimeDistance is a place-holder for the objects Y-distance to the player. However, using this code I get great inconsistencies with my results, especially when comparing devices. I suspect this is caused by distance being FPS dependant, instead of actual time dependant.
To fix this I believe I should implement Time.deltaTime in an extended manner, but I'm insecure on how.
EDIT - spawnDistanceTime related:
void Start() {
spawnTimeDistance = 4F;
}
and
Vector2 sprite_size = obstacle.GetComponent<SpriteRenderer> ().sprite.rect.size;
Vector2 spriteScale = obstacle.transform.localScale;
float sizeAndScaleY = sprite_size.y * spriteScale.y;
float obstacle_local_sprite_sizeY = (sizeAndScaleY / obstacle.GetComponent<SpriteRenderer> ().sprite.pixelsPerUnit) * 0.5F;
obstacleDimensionY = obstacle_local_sprite_sizeY;
Above code, same thing for my player.
Edited the Instantiate to include the obstacleDimensions, had them removed before as I thought it would give a simpler question.

character keeps shaking while in topdown shooter

ok well i am working on a top down shooter i have made my character look at the mouse using screentoworld view and i made my camera follow my players x, and z axis but anytime my character moves parallel from the mouse it starts shaking I think its because it looks at the mouse an the mouse real world position is dependent on the cameras position which is, but i am not sure.
here is the playerscript the many backslashes showed other things I've tried
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeed,0,Input.GetAxisRaw("Vertical")*movementspeed);
if (gameObject.GetComponent<Rigidbody> ().velocity.x != 0 && gameObject.GetComponent<Rigidbody> ().velocity.z != 0) {
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeeddiag ,0,Input.GetAxisRaw("Vertical")*movementspeeddiag);
}
// makes vector 3 type target equall to mouse position on pixial cordinates converted in to real world coordniates
target = camera.ScreenToWorldPoint (new Vector3(Input.mousePosition.x,Input.mousePosition.y,camera.transform.position.y - transform.position.y));
//Vector3 newtarget = target - transform.position;
//lerptarget = Vector3.Lerp (transform.eulerAngles,newtarget, 100);
// states the vector 3 values of target
// makes object local z face target and iniziates up axis
//Quaternion rotation = Quaternion.LookRotation (newtarget, Vector3.up);
//transform.eulerAngles = Vector3.up * Mathf.MoveTowardsAngle (transform.eulerAngles.y, rotation.eulerAngles.y, rotatespeed * Time.deltaTime);
transform.LookAt (target,Vector3.up);
and camera script
void LateUpdate(){
position = new Vector3 (player.transform.position.x, 10, player.transform.position.z);
transform.position = position;
//transform.localPosition = Vector3.Lerp (transform.position, position, speed *Time.deltaTime);
transform.eulerAngles = new Vector3 (90,0,0);
}
Probably, the problem is that you change the velocity of rigidbody. Becouse velocity changes applied each fixed update time interval, it doesnt match with frame updating time, and may lead to shaking. According to unity docs you should try to avoid directly changes of velocity. (http://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html)
I'd suggest you to you use MovePosition alongside with Lerp function to implement player movement.
See this:
http://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
http://docs.unity3d.com/ScriptReference/Vector3.Lerp.html

Categories

Resources