So I am making a game in unity where an object will dash in the direction (unit vector) of the player's mouse click. Based on dash speed and dash time variables I made and set in the inspector it will usually work, as in the distance dashed by the object and its velocity are usually constant. However, sometimes the dash distance of the object will be off. I attempted to troubleshoot the problem by logging the dash distance and the object's velocity and what I found was that sometimes either or both variables with be some random value for some reason I am missing. On most clicks though the variables are the same number. Here is my code:
[RequireComponent(typeof(Rigidbody2D))]
public class DashController : MonoBehaviour
{
Rigidbody2D rigidbody;
public float dashSpeed;
public float dashTime; //dash time for inspector use
float codeDashTime; //dash time for code use
bool dashing;
Vector2 direction;
Vector2 startPosition;
Vector2 endPosition;
float distance;
private void Start()
{
rigidbody = GetComponent<Rigidbody2D>();
codeDashTime = dashTime;
dashing = false;
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
//need to subtract the current postion from the target postion
direction = (Camera.main.ScreenToWorldPoint(Input.mousePosition) - this.transform.localPosition);
startPosition = this.transform.localPosition; // used to see the distance player dashes
dashing = true;
}
if (dashing)
{
Dash();
}
}
void Dash()
{
if (codeDashTime > 0)
{
rigidbody.velocity = direction.normalized * dashSpeed;
codeDashTime -= Time.deltaTime;
}
else
{
Debug.Log(rigidbody.velocity.magnitude);// used to see the velocity the player travels at
Debug.Log(direction.normalized.magnitude);
rigidbody.velocity = Vector2.zero;
endPosition = this.transform.localPosition; // used to see the distance player dashes
distance = Vector2.Distance(startPosition, endPosition);// used to see the distance player dashes
Debug.Log(distance); // used to see the distance player dashes
dashing = false;
codeDashTime = dashTime;
}
}
}
Sorry if my code is a bit jumbled. I have been moving things around a lot testing different solutions but nothing has worked. My best guess is that maybe something with the frames is messing up codeDashTime, but I am too new to unity and C# to know. Any help is appreciated, thanks.
All Physics should run on FixedUpdate and the inputs are normally used on Update, try separate them to get a better Rigidbody behaviour.
Related
using UnityEngine;
public class ThirdPersonMovement : MonoBehaviour
{
Rigidbody rb;
private Animator animator;
public float speed = 5f;
void Start() {
animator = GetComponent<Animator>();
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//Get player input based on world space.
float playerHorizontalInput = Input.GetAxisRaw("Horizontal");
float playerVerticalInput = Input.GetAxisRaw("Vertical");
//Get camera-normalized directional vectors
Vector3 forward = Camera.main.transform.forward;
Vector3 right = Camera.main.transform.right;
//Create direction-relative input vectors
Vector3 forwardRelativeVerticalInput = playerVerticalInput * forward;
Vector3 rightRelativeHorizontalInput = playerHorizontalInput * right;
Vector3 cameraRelativeMovement = forwardRelativeVerticalInput + rightRelativeHorizontalInput;
Vector3 movement = cameraRelativeMovement * speed * Time.deltaTime;
rb.MovePosition(transform.position + movement);
transform.forward = cameraRelativeMovement;
if (cameraRelativeMovement != Vector3.zero)
{
animator.SetBool("IsMoving", true);
}
else {
animator.SetBool("IsMoving", false);
}
}
}
I know similar questions have been posted before but I haven't been able to find a solution to what I'm experiencing.
I have the code above and it is linked to a character with a rigidbody component and 'is kinematic' checked. When I try to move, the character doesn't move for ages and then randomly jumps forward a random distance and doesn't do it again until I hit W again.
This has worked before when I used transform.Translate but obviously my character ran through the floor when the camera pointed down so I was trying to fix the issue.
A few things I notice about your code. You said this method used to work with transform.translate but not anymore, this tells me that your movement vectors are probably fine up until the point you changed them for this implementation (my guess is everything is the same except when you added tranform.position to your vector, since the function doesn't take speed, it takes position). You could try testing tranform.position + movement or just movement with a Debug.Log() statement, but that's probably not your issue.
Since you said this issue only started when you changed the function you use to move the player, I would try just inputting a non-changing Vector3 into the movement function: rb.movePosition(tranform.position + new Vector3(1,0,0)); or something like that, just to see if the movement function is even working.
Just wondered if there was a way to flip an animation in a game using C# in Unity. Ive tried the following, to get my character to flip direction:
this.gameObject.transform.localScale =1
...
this.gameObject.transform.localScale =-1
but it doesn't seem to work.Any ideas?
If you want to flip your sprite in the direction he is walking, then you may try this.
private void flipSprite()
{
bool runningHorizntaly = Mathf.Abs(myRigidbody2D.velocity.x) > Mathf.Epsilon;
if(runningHorizntaly)
{
transform.localScale = new Vector2(Mathf.Sign(myRigidbody2D.velocity.x), 1f);
}
}
It should flip your sprite (in a 2d game) in the right direction, and all Animations attached to your character are also flipping.
I hope it helps.
First of all, localScale is a Vector3, not a integer.
You can flip using scale/localScale of the Transform or the flipX field of SpriteRenderer. To do this right you need to check your character's velocity in x-axis every frame and set the correct flip direction.
Sample codes (not tested), assuming this is on your root player gameObject, your animator is also on this gameObject or its child gameObject, and you are moving it with rigidbody2D:
const float MIN_FLIP_THRESHOLD = 0.1f;
static readonly Vector3 FACE_RIGHT = Vector3.one;
static readonly Vector3 FACE_LEFT = new Vector3(-1, 1, 1);
Rigidbody2d rb;
void Awake() {
rb = GetComponent<Rigidbody2D>();
}
void Update(){
var horizontalV = rb.velocity.x;
if (horizontalV > MIN_FLIP_THRESHOLD) {
transform.localScale = FACE_RIGHT;
} else if (horizontalV < -MIN_FLIP_THRESHOLD) {
transform.localScale = FACE_LEFT;
}
}
Notice that when horizontalV is within -0.1 and 0.1 no extra flipping is done. This is usually what you want as you don't want your flipping to be too sensitive. Moreover, Unity Physics is too jank and if any small velocity can cause a flip, your character likely will jitter and flip back and forth sometimes. You should test this threshold value yourself to fit your need.
Yes I know this question was asked before by SuperSonyk! That post is here:
Set rigidbody.velocity to direction of mouse in Unity2d
However- the link referenced in the answer to that post is now legacy material, and I can't bring myself to understand it in Unity's current version.
My Problem: In short, I want to make my player accelerate in the direction of my cursor. My game is currently a TOP-DOWN Space Shooter in 2D. My player already looks at my mouse correctly, using cam.ScreenToWorldPoint. But relentlessly trying to add force seems futile for me, since I am fairly new to coding.
If I have any other issues in my code, It would be great if anyone could point them out!
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//================= MOVEMENT =====================
private float thrust = 0.5f;
private float maxSpeed = 10f;
public Rigidbody2D rb;
//================= AIMING =======================
public Camera cam;
Vector2 mousePos;
// Update is called once per frame: Good for INPUTS
void Update()
{
//aiming
mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
}
// Called Set amount of times. Good for PHYSICS CALCULATIONS
void FixedUpdate()
{
Move();
speedLimiter();
//aim
Vector2 lookDirection = mousePos - rb.position;
float angle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = angle;
}
//======= MY MOVE FUNCTION DOES NOT WORK. HELP? ======
void Move()
{
if (Input.GetButtonDown("Accelerate"))
{
rb.velocity = new Vector3(0, thrust, 0) * Time.deltaTime;
}
}
void speedLimiter()
{
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
}
}
}
Please summarize the edits clearly, thank you!
In general since this is a 2D rigidbody also the velocity is a Vector2 and you should probably rather use Vector2.ClampMagnitude
and then in
rb.velocity = new Vector3(0, thrust, 0) * Time.deltaTime;
do you only want to move in global Y direction? Also a velocity is already a "per second" value -> it makes no sense to multiply by Time.deltaTime if you reassign a new velocity. Since it says thrust I guess you rather wanted to add to the existing velocity.
What you would rather do is save your mouse direction you already have and do
public class PlayerController : MonoBehaviour
{
...
void FixedUpdate()
{
// I would update the aim BEFORE moving
// Use a NORMALIZED direction! Makes things easier later
Vector2 moveDirection = (mousePos - rb.position).normalized;
float angle = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = angle;
// OPTIONAL: Redirect the existing velocity into the new up direction
// without this after rotating you would still continue to move into the same global direction
rb.velocity = rb.velocity.magnitude * moveDirection;
Move(moveDirection);
speedLimiter();
}
void Move(Vector2 moveDirection)
{
if (Input.GetButtonDown("Accelerate"))
{
// Instead of re-assigning you would probably add to the existing velocity
// otherwise remove the "+" again
rb.velocity += moveDirection * thrust * Time.deltaTime;
}
}
void speedLimiter()
{
if (rb.velocity.magnitude > maxSpeed)
{
// You are working with Vector2 so use the correct method right away
rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxSpeed);
}
}
}
You already have half of the work by having your player looking at the mouse. We will be using the vector lookDirection as the direction of the movement for the player, normalize it so its length is 1 and multiply it by thrust. This way, we have a vector in the direction of the mouse and of length thrust as intended.
void Move()
{
if (Input.GetButtonDown("Accelerate"))
{
Vector2 lookDirection = (mousePos - rb.position);
rb.AddForce(lookDirection.normalized * thrust);
}
}
Note that we work here with Vector2 since you are using a Rigidbody2D.
Also, I don't think that using Time.DeltaTime is relevant here. When you apply force to a physic object, you don't need to use it, by definition, the function FixedUpdate() has a fixed frequency. If you look at one of Unity's tutorial, you can see that they don't multiply it by Time.DeltaTime
Last tip: you may want to be playing around with the drag property of your Rigidbody2D to have your player slow down over time when not accelerating, otherwise, it will be sliding out of control and the player won't be able to stop it.
Don't hesitate to tell me if my answer is not clear for you or does not entirely satisfy you, it's my first answer here!
EDIT: I forgot to tell you but as derHugo mentioned, since you are using Rigidbody2D, you must use Vector2.ClampMagnitude to limit your speed in your speedLimiter function. Also, I don't think that you need your if since the function Vector2.ClampMagnitude will only change the value of the velocity if it exceeds the maximum.
Thanks to everyone who responded!
With the help of Brackeys community, I managed to solve this by using this method:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//================= MOVEMENT =====================
private float thrust = 10f;
private float maxSpeed = 100f;
public Rigidbody2D rb;
//================= AIMING =======================
public Camera cam;
Vector2 mousePos;
// Update is called once per frame: Good for INPUTS
void Update()
{
//aiming
mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
}
// Called Set amount of times. Good for PHYSICS CALCULATIONS
void FixedUpdate()
{
Move();
speedLimiter();
//aim
Vector2 lookDirection = mousePos - rb.position;
float angle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = angle;
}
void Move()
{
if (Input.GetKey("up"))
{
rb.AddRelativeForce(Vector2.up*thrust);
}
}
void speedLimiter()
{
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
}
}
//rb.AddForce(new Vector3(0, thrust, 0));
}
So my game is sort of like a 3d top down shooter, so I want my gun to shoot wherever the mouse is and it wont go to the mouse unless im shooting down. If you've seen brackyes game called ball wars, im sort of trying to replicate one like that but the projectile is not shooting the right way.
I got my script from blackthornprods ranged combat tutorial (which is for 2d so maybe thats the issue but I dont know how to solve it) :
public float speed;
public float lifeTime;
private void Start()
{
Invoke("DestoryProjectile", lifeTime);
}
private void Update()
{
transform.Translate(transform.up * speed * Time.deltaTime);
}
void DestroyProjectile()
{
Destroy(gameObject);
}
Appreciate anyone to try!
Here is my other script:
Camera mainCam;
public GameObject projectile;
public Transform shotPoint;
private float timeBtwShots;
public float startTimeBtwShots;
void Awake()
{
mainCam = Camera.main;
}
void Update()
{
float objectDepthFromCamera = Vector3.Dot(
transform.position - mainCam.transform.position,
mainCam.transform.forward);
Vector3 cursorWorldPosition = mainCam.ScreenToWorldPoint(Input.mousePosition
+ Vector3.forward * objectDepthFromCamera);
Vector3 localUpNeeded = Vector3.Cross(Vector3.forward,
cursorWorldPosition - transform.position);
transform.rotation = Quaternion.LookRotation(Vector3.forward, localUpNeeded);
if(timeBtwShots <= 0)
{
if (Input.GetMouseButtonDown(0))
{
Instantiate(projectile, shotPoint.position, transform.rotation);
timeBtwShots = startTimeBtwShots;
}
}
else
{
timeBtwShots -= Time.deltaTime;
}
}
Projectile not shooting direction of my weapon. Simple solution -
First instantiate or pool the instance of projectile.
Set rotation of projection from the rotation of weapon & set location to spawn point
Now fire, or whatever strategy you are using
Consider Global rotation, if you need help, tell me, I will edit and give a snippet of code.
This should work. If doesn't post all necessary code, I will give a better solution.
Here is sample github project I created, just for you. I opened Unity nearly after a year. Please check all the versions.
Must check :
firing in facing direction 💋
just instantiate at spawn point
just added some rotation
I think this should give you concept.
Press X for a rantom rotation
Press Space to shoot a projectile :lol:
The white cube shows that it always shoots at a constant direction
I'm new to Unity, so probably this is a dumb question but I have several days trying to figure it out. I have an enemy walking from one side of a platform to the other. I'm using Raycasting to check if there´s no ground below and turn to the other way.
What I want is for the enemy to walk AROUND the platform (Just like the enemies in Metroid). I set the gravity of the enemy to 0 so it will not fall down, but I don´t know how to make it rotate and keep walking around the platform. I know it could be done with raycasting but I have not clue how to do it.
I will enormously appreciated if anyone can help me with the piece of code I'm missing. Thanks in advance!
public class BlobController : MonoBehaviour {
private Rigidbody2D myRigidBody;
public float moveSpeed;
public LayerMask groundMask;
float myWidth;
// Use this for initialization
void Start ()
{
myRigidBody = GetComponent<Rigidbody2D>();
myWidth = GetComponent<SpriteRenderer> ().bounds.extents.x;
}
// Update is called once per frame
void Update ()
{
}
void FixedUpdate()
{
//Check to see if there's ground in front before moving forward
Vector2 lineCasPos = transform.position - transform.right * myWidth;
Debug.DrawRay (lineCasPos, Vector2.down,Color.red);
bool isGrounded = Physics2D.Raycast (lineCasPos, Vector2.down, 2, groundMask);
//If there's no ground, turn around
if (!isGrounded)
{
Vector3 currRot = transform.eulerAngles;
currRot.y += 180;
transform.eulerAngles = currRot;
}
//Always move forward
Vector2 myVel = myRigidBody.velocity;
myVel.x = -transform.right.x * moveSpeed;
myRigidBody.velocity = myVel;
}
}