I am following along with creating the Obstacle Game from Game Programming with Unity and C# by Casey Hardman. I have reached the point in the first few pages of Chapter 16 where you create a Hazard to kill the player. In the beginning stages, you write code that kills the player object if the player object collides with the hazard. I've followed the instructions (as far as I can tell) to the letter and yet when I created a sphere as a test hazard, assigned the script to it, and ran the player object into it, nothing happened when they collided. I thought maybe there was an error with the Hazard code, so I commented out the "when collide with object on player layer, kill player" code, wrote code to have it just write to the console when they collide, and tested it. Unfortunately, there doesn't seem to be any collision detection when these two objects touch each other. I've Googled "objects not colliding unity" and every combination of "collision not detected in unity" I can think of and none of the answers have helped so I'm posting here in hopes I get an answer. I'm including screenshots of the two objects and their settings, my physics settings in Unity, and the code I've written for both objects in hopes that someone can catch what I'm doing wrong.
The Player Object
The Test Hazard Object
Layer Collision Matrix
The Player Object Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
//References
[Header("References")]
public Transform trans;
public Transform modelTrans;
public CharacterController characterController;
//Movement
[Header("Movement")]
[Tooltip("Units moved per second at maximum speed.")]
public float movespeed = 24;
[Tooltip("Time, in seconds, to reach maximum speed.")]
public float timeToMaxSpeed = .26f;
private float VelocityGainPerSecond{ get { return movespeed / timeToMaxSpeed; }}
[Tooltip("Time, in seconds, to go from maximum speed to stationary.")]
public float timeToLoseMaxSpeed = .2f;
private float VelocityLossPerSecond { get { return movespeed / timeToLoseMaxSpeed; }}
[Tooltip("Multiplier for momentum when attempting to move in a direction opposite the current traveling direction (e.g. trying to move right when already moving left.")]
public float reverseMomentumMultiplier = 2.2f;
private Vector3 movementVelocity = Vector3.zero;
private void Movement()
{
// IF W or the up arrow key is held:
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
if (movementVelocity.z >= 0) // If we're already moving forward
//Increase Z velocity by VelocityGainPerSecond, but don't go higher than 'moveSpeed':
movementVelocity.z = Mathf.Min(movespeed,movementVelocity.z + VelocityGainPerSecond * Time.deltaTime);
else // Else if we're moving back
//Increase Z velocity by VelocityGainPerSecond, using the reverseMomentumMultiplier, but don't raise higher than 0:
movementVelocity.z = Mathf.Min(0,movementVelocity.z + reverseMomentumMultiplier * Time.deltaTime);
}
//If S or the down arrow key is held:
else if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
if (movementVelocity.z > 0) // If we're already moving forward
movementVelocity.z = Mathf.Max(0,movementVelocity.z - VelocityGainPerSecond * reverseMomentumMultiplier * Time.deltaTime);
else // If we're moving back or not moving at all
movementVelocity.z = Mathf.Max(-movespeed,movementVelocity.z - VelocityGainPerSecond * Time.deltaTime);
}
else //If neither forward nor back are being held
{
//We must bring the Z velocity back to 0 over time.
if (movementVelocity.z > 0) // If we're moving up,
//Decrease Z velocity by VelocityLossPerSecond, but don't go any lower than 0:
movementVelocity.z = Mathf.Max(0,movementVelocity.z - VelocityLossPerSecond * Time.deltaTime);
else //If we're moving down,
//Increase Z velocity (back towards 0) by VelocityLossPerSecond, but don't go any higher than 0:
movementVelocity.z = Mathf.Min(0,movementVelocity.z + VelocityLossPerSecond * Time.deltaTime);
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
if (movementVelocity.x >= 0) //If we're already moving right
//Increase X velocty by VelocityGainPerSecond, but don't go higher than 'movespeed':
movementVelocity.x = Mathf.Min(movespeed,movementVelocity.x + VelocityGainPerSecond * Time.deltaTime);
else //If we're moving left
//Increase X velocity by VelocityGainPerSecond, using the reverseMomentumMultiplier, but don't raise higher than 0:
movementVelocity.x = Mathf.Min(0,movementVelocity.x + VelocityGainPerSecond * reverseMomentumMultiplier * Time.deltaTime);
}
else if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
if (movementVelocity.x > 0) //If we're already moving right
movementVelocity.x = Mathf.Max(0,movementVelocity.x - VelocityGainPerSecond * reverseMomentumMultiplier * Time.deltaTime);
else // If we're moving left or not at all
movementVelocity.x = Mathf.Max(-movespeed,movementVelocity.x - VelocityGainPerSecond * Time.deltaTime);
}
else //If neither left nor right are being held
{
//We must bring the X Velocity back to 0 over time.
if (movementVelocity.x > 0) //If we're moving right,
//Decrease X velocity by VelocityLossPerSecond, but don't go any lower than 0:
movementVelocity.x = Mathf.Max(0,movementVelocity.x - VelocityLossPerSecond * Time.deltaTime);
else //If we're moving left
//Increase X velocity (back towards 0) by VelocityLossPerSecond, but don't go any higher than 0:
movementVelocity.x = Mathf.Min(0,movementVelocity.x + VelocityLossPerSecond * Time.deltaTime);
}
//If the player is moving in either direction (left/right or up/down):
if (movementVelocity.x != 0 || movementVelocity.z != 0)
{
//Applying the movement velocity:
characterController.Move(movementVelocity * Time.deltaTime);
//Keeping the model holder rotated towards the last movement direction:
modelTrans.rotation = Quaternion.Slerp(modelTrans.rotation, Quaternion.LookRotation(movementVelocity), .18F);
}
}
//Death and Respawning
[Header("Death and Respawning")]
[Tooltip("How long after the player's death, in seconds, before they are respawned?")]
public float respawnWaitTime = 2f;
private bool dead = false;
private Vector3 spawnPoint;
private Quaternion spawnRotation;
private void Update()
{
Movement();
}
void Start()
{
spawnPoint = trans.position;
spawnRotation = modelTrans.rotation;
}
public void Die()
{
if (!dead)
{
dead = true;
Invoke("Respawn", respawnWaitTime);
movementVelocity = Vector3.zero;
enabled = false;
characterController.enabled = false;
modelTrans.gameObject.SetActive(false);
}
}
public void Respawn()
{
modelTrans.rotation = spawnRotation;
dead = false;
trans.position = spawnPoint;
enabled = true;
characterController.enabled = true;
modelTrans.gameObject.SetActive(true);
}
}
The Hazard Object Script:
`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hazard : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.layer == 8)
{
//Player player = other.GetComponent<Player>();
//if (player != null)
//player.Die();
Debug.Log("Yay!");
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}`
I have tried:
Adding a separate box collider to the Player Object
Adding a Rigidbody to one, both, and the other objects
Pulling my hair out
Restarting Unity
Restarting my computer
Tagging the Player Object as Player (I saw a question that looked similar to mine so I thought maybe this would help; I'm not very familiar with Unity or C# yet so I'm unsure what or why things would help.)
None of the above has made it seem like Unity is detecting collision between these two objects.
Here's a line from the documentation for OnTriggerEnter:
"Both GameObjects must contain a Collider component. One must have Collider.isTrigger enabled, and contain a Rigidbody."
Have you tried adding the Collider to your player at the same time as the Rigidbody component to your hazard?
For OnTriggerEnter() you need colliders with Is Trigger ticked and a ridig body. But this check
if (other.gameObject.layer == 8)
does not what it looks like. Layer values are used as bit masks, read more about it here.
To check against the correct layer, you need bitwise operations:
int playerMask = LayerMask.GetMask("Player"); // something like 00110101001
int otherLayer = 1 << other.gameObject.layer; // something like 00010000000
if ((playerMask | otherLayer ) != 0) { // we hit the mask ...1.......
Debug.Log("yay");
}
Be careful when using bitwise operators to not get tricked by operator precedences. Short version from above would be
if ((playerMask | (1 << other.gameObject.layer)) != 0) { ... // extra parentheses!
This will succeed for all objects within the player layer. An alternative would be to set the tag of the player object and compare against that tag:
if (other.gameObject.CompareTag("Player")) { ... // "Player" tag is not the same as "Player" layer
Related
i can't understand how can i make it so that my camera will approach an object and after reaching a certain distance from it,it will go back to the initial position, and so on.
Here is my code:
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
float speed = 10f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 distance = (transform.position - cameraLook.transform.position);
Debug.Log(distance);
transform.LookAt(cameraLook.transform.position);
if (distance.x > 30)
{
go();
}
if (distance.x < 0)
{
goBack();
}
}
void go()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
void goBack()
{
transform.position-= transform.forward * speed * Time.deltaTime;
}
}
Update is called once every frame.
What currently happens in your code is
Frame 1
You reach a distance of x > 30
you call go once
Frame 2
Now your distance might be < 30 but still > 0 due to the movement last frame
You do nothing at all anymore
or the same with
Frame 1
You reach a distance of < 0
you call go once
Frame 2
Now your distance might be > 0 but still < 30 due to the movement last frame
You do nothing at all anymore
Then also it is very "dangerous" / unreliable to simply check the global X axis difference between your objects. This assumes that your camera is definitely moving along the X axis.
Instead I would rather use something like e.g.
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
[Toolbox("The minimum distance the camera should stay away from the object when moving closer")]
[Min(0f)] public float minDistance = 0f;
[Toolbox("The maximum distance the camera should move away from the object")]
[Min(0f)] public float maxDistance = 30f;
[Toolbox("How fast shall the camera travel. If your target objects moves have in mind that this needs to be bigger then the movement speed of your target object ;)")]
[Min(0f)] public float moveUnitsPerSecond = 10f;
[Toolbox("If true starts moving towards otherwise starts moving away")]
public bool startMovingTowards = true;
private bool isMovingTowards;
private void Start()
{
isMovingTowards = startMovingTowards;
}
void Update()
{
// first look at the target object
transform.LookAt(cameraLook.transform.position);
// get the desired final distance based on the move direction flag
var targetDistance = isMovingTowards ? minDistance : maxDistance;
// get the desired final position based on the desired distance
// simply moving the distance backwards away from the object
var targetPosition = cameraLook.transform.position - transform.forward * targetDistance;
// Move smoothly with the given moveUnitsPerSecond to the desired final position
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond * Time.deltaTime);
// is the desired final position reached (within a precision of 0.00001)
if(transform.position == targetPosition)
{
// then simply invert the move direction
isMovingTowards = !isMovingTowards;
}
}
}
I've been trying to realistically reflect a 3d sphere on the walls of a box for a while now in Unity. For some reason, the reflection is generally correct, but when the ball hits a wall in certain directions, the reflection is incorrect.
To illustrate what happens to the ball upon hitting a wall: T = top wall, R = right wall, L = left wall, and B = bottom wall. Let r = the ball comes/goes to the right, l = for the left, and s = the ball stops/slows down significantly. The instructions below take this format: Xyz, where X = the wall the ball is about to hit, y = the ball's initial direction, z = the reflection. The game has a top-down perspective, and the instructions are based on the wall's perspective. I'm also new to C#, so the code is potentially eye burning.
Instructions: Tll, Trl; Bll, Brl; Rls or after hitting another wall Rlr, Rrl; Lls or after hitting another wall Llr, Lrl
Generally, when the ball stops, it jumps in the air. I wonder if this is because the angle reflects along the wrong axis, but why would this only sometimes happen? Also, when only one key is held, the ball bounces back and forth until it leaves the arena. I know about discrete and continuous hit detection, and the setting is on discrete, but the walls generally contain the ball well enough, with this case being the exception.
What I tried:
Figuring out how to use Vector3.Reflect. I do not understand what variables this function should contain and how to apply this to my code. I did look at the Unity Documentation page for help, but it did not answer my question.
Changing the negative signs, as the angle has to be a reflection on the y-axis, and this does change how the reflections work, but does not solve the problem. The current way the negatives are ordered are the most ideal I found.
Giving the ball a physics material for bounciness.
Adding a small number to the denominator in the arctan equation to help prevent a division by zero. This did not help at all.
Creating different equations (basically changing the negatives) for varying combinations of positive and negative accelerations. Since there is a certain positive or negative acceleration associated with each button press (see Movement script), and there seems to be an issue with said signs, I wondered if associating each acceleration with its own set of equations could solve the problem. It did not work.
Checked if the walls were at different angles.
Deleting the variables xA and yA, or placing them in different spots.
Experimented with finding the speed of the object, but had no idea how to implement it.
The code for the Movement script for the player named Controller:
public class Movement : MonoBehaviour
{
public static float xAcceleration = 0.0f;
public static float yAcceleration = 0.0f;
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.W)) //If the key W is pressed:
{
Vector3 position = this.transform.position; //Variable position is set to transform the players placement in the game.
if (yAcceleration >= -5 && yAcceleration <= 5) //If the y vector of the acceleration is >= -5 and <= 5:
{
yAcceleration = yAcceleration + 0.01f; //The y vector of the acceleration increases by 0.01 as long as the key W is pressed.
}
position.z = position.z + (0.1f * yAcceleration); //The position of the object on the z-axis (pretend it is the y-axis in the game world) is transformed by its original position plus its speed times its yAcceleration.
this.transform.position = position; //The gameObject is now transformed to a position equal to the variable position by the z-axis.
}
else //If the key W is let go of:
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position; //The position of the gameObject continues to update, but its acceleration does not change. Basically, it continues to move forward.
}
//The rest of the code is very similar to the above, but I included it just in case there was something wrong.
if (Input.GetKey(KeyCode.S))
{
Vector3 position = this.transform.position;
if (yAcceleration >= -5 && yAcceleration <= 5)
{
yAcceleration = (yAcceleration) - 0.01f;
}
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.A))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) - 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.D))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) + 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
}
}
This is the code for the collider and reflection:
public class Collider : MonoBehaviour
{
public float xA;
public float yA;
void OnCollisionEnter(Collision collision) //If a gameObject enters the collision of another object, this immediately happens once.
{
if (gameObject.tag == "Boundary") //If the gameObject has a tag named Boundary:
{
yA = -Movement.yAcceleration; //yA stores the value of yAcceleration after being called from script Movement as a negative. Its a reflection.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration)); //xAcceleration is changed based on this equation: A * artan(A_y / A_x). The 0.000001 was here, adding to A_x to help prevent a 0 as the denominator.
xA = Movement.xAcceleration; //This is declared now...
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA)); //This uses xA because Movement.xAcceleration is changed, and the yAcceleration calculation is based on the xAcceleration prior the collision.
}
}
void OnCollisionStay(Collision collision)
{
if (gameObject.tag == "Boundary")
{
yA = Movement.yAcceleration; //The same thing happens as before.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));
xA = Movement.xAcceleration;
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));
Movement.xAcceleration = -Movement.xAcceleration / 2; //On collision, the ball is reflected across the x-axis at half its speed.
Movement.yAcceleration = Movement.yAcceleration / 2; //yAcceleration is half its original value.
}
}
}
The picture below is the game setup. I apologize that it is a link; I do not have enough Reputation to merit a loaded image on this page. Also, if anything is unclear, please message me.
https://i.stack.imgur.com/VREV4.png
I would really appreciate the help. Thanks!
One very important note here: As soon as there is any Rigidbody involved you do not want to set any values through the .transform - This breaks the physics and collision detection!
Your Movement should rather alter the behavior of the Rigidbody e.g. by simply changing its Rigibody.velocity
I would then also place the collision check directly into the balls's component and check whether you hit a wall ("Boundary")
Then another note: Your code is currently frame-rate dependent. It means that if your target device runs with only 30 frames per second you will add 0.3 per second to the acceleration. If you run however on a more powerful device that manages to run with 200 frames per second then you add 2 per second.
You should rather define the de/increase per second and multiply it by Time.deltaTime
All together maybe something like this
public class Movement : MonoBehaviour
{
// Adjust these settings via the Inspector
[SerializeField] private float _maxMoveSpeed = 5f;
[SerializeField] private float _speedIncreasePerSecond = 1f;
// Already reference this via the Inspector
[SerializeField] private Rigidbody _rigidbody;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
// Get User Input in Update
private void Update()
{
var velocity = _rigidbody.velocity;
velocity.y = 0;
if (Input.GetKey(KeyCode.W) && velocity.z < _maxMoveSpeed)
{
velocity.z += _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.S) && velocity.z > -_maxMoveSpeed)
{
velocity.z -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A) && velocity.x > -_maxMoveSpeed)
{
velocity.x -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.D) && velocity.x < _maxMoveSpeed)
{
velocity.x += _speedIncreasePerSecond * Time.deltaTime;
}
// clamp to the max speed in case you move diagonal
if(velocity.magnitude > _maxMoveSpeed)
{
velocity = velocity.normalized * _maxMoveSpeed;
}
_rigidbody.velocity = velocity;
}
}
And then finally simply add a PhysicsMaterial with desired settings to the walls and ball.
I used Friction = 0f and Bounciness = 0.7f for ball and walls. For slow movements you also might want/have to adjust the Bounce Threshold in the Project's Physics Settings otherwise there will be no bouncing if the velocity is smaller then 2 by default.
This depends a bit on your definition of "realistic". I disabled gravity so the ball also has no rotation and angular friction:
I'm using this:
void Update(){
if(leftJoystick){
rb.AddRelativeForce(0, 0, 400f, ForceMode.Impulse);
}
}
to move my character forward in the air (using jetpack). When I let go of the joystick my character stops immediately. What I want is that the character keeps moving forward after joystick is released and with time slow down and gradually stop. It looks stupid that my character stops like it hit a wall. What kind of force/velocity/transform I should use so that physics slow down my character after I let go of the joystick?
My rigidbody settings:
Mass: 100
Drag: 0
Angular drag: 0.05
Uses gravity
Not kinematic
Interpolate: interpolate
Collision detection: Discrete
Freeze position: none
Freeze rotation: all
Character also has a capsule collider that is NOT trigger and has no physics material.
EDIT: here is the full code so you can see if there is any conflicts in my code
using UnityEngine;
using System.Collections;
using UnityStandardAssets.CrossPlatformInput;
public class JetpackController : MonoBehaviour{
Rigidbody rb;
// controller axes
[SerializeField]
public string speedAxis = "Vertical";
public string rotateAxis = "HorizontalRotate";
[SerializeField]
public float forwardSpeed;
public float upSpeed;
public float rotateSpeed;
public float rotationIgnoreZone;
public float MAX_HEIGHT;
float speedInput;
float rotateInput;
public bool leftJoystick;
public bool rightJoystick;
ParticleSystem jetpackFire1;
Vector3 originalGravity;
// access Joystick script in Standard assets to detect when right joystick is held down
Joystick joystick_left;
Joystick joystick_right;
void Start () {
if (GetComponent<Rigidbody> ()) {
rb = GetComponent<Rigidbody> ();
} else {
Debug.LogError ("Player needs to have a rigidbody.");
}
// get Joysticks
if (GameObject.Find ("MobileJoystick_left").GetComponent<Joystick> ()) {
joystick_left = GameObject.Find ("MobileJoystick_left").GetComponent<Joystick> ();
} else {
Debug.LogError ("Either gameobject with name 'MobileJoystick_right' does not exist in the scene or it does not have script called Joystick.cs attached to it.");
}
if (GameObject.Find ("MobileJoystick_right").GetComponent<Joystick> ()) {
joystick_right = GameObject.Find ("MobileJoystick_right").GetComponent<Joystick> ();
} else {
Debug.LogError ("Either gameobject with name 'MobileJoystick_right' does not exist in the scene or it does not have script called Joystick.cs attached to it.");
}
jetpackFire1 = GameObject.Find("JetpackFire1").GetComponent<ParticleSystem> ();
jetpackFire1.playbackSpeed = 5.0f;
originalGravity = Physics.gravity;
}
void FixedUpdate () {
GetInput ();
// move forward and backward according to left joysticks input
if(leftJoystick){
// left joystick held down
rb.AddRelativeForce(0, 0, forwardSpeed*speedInput, ForceMode.Impulse);
}
// if right joystick is used
if (rightJoystick) {
jetpackFire1.Play ();
if (transform.position.y < MAX_HEIGHT) {
// allow going up
//rb.AddRelativeForce (0, upSpeed, 0, ForceMode.Impulse);
rb.AddForce(transform.forward * forwardSpeed);
} else if (transform.position.y >= MAX_HEIGHT) {
// prevent player to go any further up and also falling
Physics.gravity = Vector3.zero; // no gravity to prevent player from falling
rb.velocity = Vector3.zero;
}
// rotate
// always keep rotating (player can go past look at joystick dir)
if (rotateInput >= rotationIgnoreZone || rotateInput <= -rotationIgnoreZone) {
transform.Rotate(Vector3.up * rotateInput, Time.deltaTime * rotateSpeed);
}
} else {
jetpackFire1.Stop ();
// if right joystick is released
Physics.gravity = originalGravity; // normal gravity -> player can fall
}
}
public void GetInput(){
speedInput = CrossPlatformInputManager.GetAxis (speedAxis);
rotateInput = CrossPlatformInputManager.GetAxis (rotateAxis);
// access Joystick.cs script to grab wether right joystick is pressed or not
leftJoystick = joystick_left.leftJoystickPressed;
rightJoystick = joystick_right.rightJoystickPressed;
}
}
Can't comment yet
Like J.Small said, you could multiply the velocity with a number around 0.95 to slowly slow the rigidbody down. If you put the multiplication in the Update method I suggest you to multiply the ~0.95 number with Time.deltaTime, so the velocity decrease will be proportional to the passed time.
void Update(){
if(leftJoystick){
rb.AddRelativeForce(0, 0, 400f, ForceMode.Impulse);
}
if(noJoystick){
rb.velocity = rb.velocity * 0.95 * Time.deltaTime;
//Or: rb.velocity.x
}
}
You could also apply a small amount of force instead of multiplying the velocity if you want to slow down the rigidbody in a more configurable way (the amount should be based on Time.deltaTime if you put it into Update - which gets called once a frame), but the method above is easier.
I'm not a pro at coding, so I'm not sure if this'll work for everyone, but I tried something like this to fix a similar issue.
void FixedUpdate()
{
playerRigidbody.AddRelativeForce(Vector2.up * forwardInput * playerSpeed * Time.fixedDeltaTime);
if (forwardInput <= 0)
{
playerRigidbody.velocity = playerRigidbody.velocity / 1.005f; // <-- This will gradually slow down the player when they're idle.
}
}
You could do a simple linear slow down.
void Update(){
if(leftJoystick){
rb.AddRelativeForce(0, 0, 400f, ForceMode.Impulse);
}
if(noJoystick){
rb.velocity = rb.velocity * 0.9;
}
}
Note that the update function gets called on frame update, so I would suggest putting this in a FixedUpdate() function that way the slow rate won't depend on the frame rate.
I am trying to achieve basic racing game. Infinite racing game, movement method like a subway surfers. I have a problem about changing lane. I dont want to teleport to other lane, I want to smoothly. I am newbee in unity, I have try Lerp method but it is not working.
using UnityEngine;
using System.Collections;
public class VehicleController : MonoBehaviour
{
public float drift;
public Vector3 positionA;
public Vector3 positionB;
public Vector3 positionC;
public Vector3 positionD;
private Transform tf;
private Rigidbody rb;
private Vector3 vehiclePos;
void Awake()
{
//rb = GetComponent<Rigidbody> ();
tf = transform;
}
void Update()
{
vehiclePos = tf.position;
if (Input.GetKey( KeyCode.Space ))
DecreaseSpeed ();
else
IncreaseSpeed ();
if (Input.GetKeyDown (KeyCode.A))
{
MoveToRight ();
Debug.Log( "Move to Right!" );
}
if (Input.GetKeyDown (KeyCode.D))
{
MoveToLeft ();
Debug.Log( "Move to Left!" );
}
}
void FixedUpdate()
{
tf.Translate (Vector3.forward * speed * Time.deltaTime);//My Movement Method.
}
void MoveToLeft()
{
if (vehiclePos.position.x == positionA.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionB, Time.deltaTime * drift);
}
void MoveToRight()
{
if (vehiclePos.position.x == positionB.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionA, Time.deltaTime * drift);
}
}
First: Don't use == for position.x, since it's a floating-point (decimals) value and in this case it would be very rare for it to return "true". Here's some info about comparing floats.
Second: It doesn't look like you're connecting your actual position with vehiclePos anywhere. transform.position is what you want there.
Third: Input.GetAxis() is a cleaner way to deal with direction input. Instead of specifically calling out each button you can deal with just one float value between -1 and 1. It will also let you reconfigure the keys easily.
Fourth: In an infinite runner it is better to have the world move towards your character and camera than to have the character and camera actually move forward. Floating point numbers get less precise as you move further away from zero, so you should have your action take place relatively close to the world origin (0,0,0) point if you can.
If you want to press the button once to change lanes, you should keep an integer variable that saves which lane you're currently in. If you press LEFT you subtract one, and if you press RIGHT you add one. You should also add a check to make sure it stays within the desired range.
Then in Update() you just need to ALWAYS Lerp towards that X value. You can use Mathf.Lerp to lerp only one variable at a time if you want.
public int laneNumber = 0;
public int lanesCount = 4;
bool didChangeLastFrame = false;
public float laneDistance = 2;
public float firstLaneXPos = 0;
public float deadZone = 0.1f;
public float sideSpeed = 5;
void Update() {
//"Horizontal" is a default input axis set to arrow keys and A/D
//We want to check whether it is less than the deadZone instead of whether it's equal to zero
float input = Input.GetAxis("Horizontal");
if(Mathf.Abs(input) > deadZone) {
if(!didChangeLastFrame) {
didChangeLastFrame = true; //Prevent overshooting lanes
laneNumber += Mathf.roundToInt(Mathf.Sign(input));
if(laneNumber < 0) laneNumber = 0;
else if(laneNumber >= lanesCount) laneNumber = lanesCount - 1;
}
} else {
didChangeLastFrame = false;
//The user hasn't pressed a direction this frame, so allow changing directions next frame.
}
Vector3 pos = transform.position;
pos.x = Mathf.Lerp(pos.x, firstLandXPos + laneDistance * laneNumber, Time.deltaTime * sideSpeed);
transform.position = pos;
}
You could likely just use this code as-is, but I suggest you look it over and try to figure out an understanding of how and why it works. A newbie today who always seeks to improve their skill can do something amazing next week. Hope this helps. :)
My unity game is a spring and when you hold the space key the spring moves back. When you release the space bar the spring releases depending on the amount of torque built up when holding down space bar.
When it is released the spring just overlaps inside the sphere I need the ball to move forward faster or slower depending on the amount of power in the Power variable.
I cannot use Rigidbody.Addforce as the spring needs to cause the ball to move.
Script Notes:
-> If the spring is fully out (released) it should have a value of -2 If it is held in to its maximum torque it should have an x axis value of -2.2
using UnityEngine;
using System.Collections;
public class SpringMechanics : MonoBehaviour {
public bool SpringIn;
public float Power;
public bool SpringReleased;
void Update()
{
// IF THE SPRINGS X POSITION IS AT -2 OR ABOVE THAT VALUE IT WILL MARK THE SPRING IN VARIBALE AS TRUE
if (transform.position.x >=-0.5f)
{
SpringIn = false;
}
else
{
SpringIn = true;
}
//CHECKS IF MOUSE SPACE BUTTON IS DOWN AND IF SO MOVES SPRING IN
if (Input.GetKey (KeyCode.Space) && transform.position.x <= 2.3) {
Debug.Log ("Spring Being Compressed");
// Moves Spring Backwards when space key is down
//rigidbody.velocity = new Vector3(rigidbody.velocity.x - 0.05f * Time.deltaTime,rigidbody.velocity.y,rigidbody.velocity.z);
transform.position = (new Vector3 (transform.position.x - 0.05f * Time.deltaTime, transform.position.y, transform.position.z));
// Adds 3 Units Of Power Every Second While Key Is Down
Power += 2 * Time.deltaTime;
}
//Releases Ball When Space Bar Is Up After Being Down
if (Input.GetKeyUp (KeyCode.Space))
{
SpringReleased = true;
}
if (SpringReleased == true)
{
FireBall();
}
}
//When Firing Ball Executer Below Function
void FireBall()
{
if (transform.position.x <= -2){
rigidbody.velocity = new Vector3(rigidbody.velocity.x + Power * Time.deltaTime,rigidbody.velocity.y,rigidbody.velocity.z);
//transform.position = (new Vector3 (transform.position.x + Power * Time.deltaTime, transform.position.y, transform.position.z));
transform.position = (new Vector3 (-2, transform.position.y, transform.position.z));
if (transform.position.x >= -2 && SpringReleased == true){
SpringReleased=false;
Power = 0;
rigidbody.velocity = new Vector3(0,0,0);
Debug.Log ("Reset Spring");
}
}
}
}
How can I make it be hit forward towards the right using the spring depending on the value of of the power variable?
Apply the force to the spring (or some part of the spring, like a separate mesh at the top) instead and the physics engine should do the rest.