I'm making a top down shooter game in unity using the old input system and I ran into a problem. I use the arrow keys to shoot but I'm not sure how to fire in multiple directions at the same time/frame.
Code:
void Update()
{
currentTime += Time.deltaTime;
//input section
if (Input.GetKey(KeyCode.UpArrow))
{
ShootUp();
}
if (Input.GetKey(KeyCode.DownArrow))
{
ShootDown();
}
if (Input.GetKey(KeyCode.LeftArrow))
{
ShootLeft();
}
if (Input.GetKey(KeyCode.RightArrow))
{
ShootRight();
}
if (Input.GetKey(KeyCode.UpArrow) && Input.GetKey(KeyCode.RightArrow))
{
ShootUp();
ShootRight();
}
}
I thought that looking at both arrow keys at the same time, I would just call both functions as well. Its does not work however.
I tried google but it's pretty hard to find info on the old input system having the same problem.
Edit for clarification
public void SpawnBullet(Vector3 direction, Quaternion rotation)
{
//fire rate correction
if (currentTime < currentFireRate) { return; };
currentTime = 0;
//bullet spawning and firing
Bullet instanceOfBullet = Instantiate(BulletPrefab);
instanceOfBullet.transform.position = transform.position;
instanceOfBullet.direction = direction;
instanceOfBullet.transform.rotation = rotation;
instanceOfBullet.tag = "Player Bullet";
}
void ShootUp()
{
SpawnBullet(Vector3.up, Quaternion.Euler(0,0,0));
//Debug.Log("bullet");
}
Updated answer
The problem is that currentTime becomes 0 al in the first method, and in the next method, being less than currentFireRate, the method stops by not instantiating anything.
To solve this, I created a Coroutine that takes an Action as a parameter, that is any code snippet.
Start this coroutine after checking if currentTime is less than currentFireRate, and give it as an update the code instruction that assigns currentTime the value 0.
The coroutine first waits for a frame, and then invokes the action, executing the indicated code (in our case set currentTime equal to 0).
In this way, if you press two keys while executing the method to instantiate the bullet twice, the value of currentTime will not set to 0 before the end of the frame and therefore both will be executed.
void Update()
{
currentTime += Time.deltaTime;
//input section
if (Input.GetKey(KeyCode.UpArrow))
{
ShootUp();
}
if (Input.GetKey(KeyCode.DownArrow))
{
ShootDown();
}
if (Input.GetKey(KeyCode.LeftArrow))
{
ShootLeft();
}
if (Input.GetKey(KeyCode.RightArrow))
{
ShootRight();
}
}
public void SpawnBullet(Vector3 direction, Quaternion rotation)
{
//fire rate correction
if (currentTime < currentFireRate) { return; };
StartCoroutine(DoActionAfterAFrame(() => currentTime = 0));
//bullet spawning and firing
Bullet instanceOfBullet = Instantiate(BulletPrefab);
instanceOfBullet.transform.position = transform.position;
instanceOfBullet.direction = direction;
instanceOfBullet.transform.rotation = rotation;
instanceOfBullet.tag = "Player Bullet";
}
void ShootUp()
{
SpawnBullet(Vector3.up, Quaternion.Euler(0, 0, 0));
}
IEnumerator DoActionAfterAFrame(System.Action _action)
{
yield return new WaitForEndOfFrame();
_action.Invoke();
}
I hope I have helped you :)
Okay, so first of all. All of these should go into a seperate method and then called in Update. Secondly, Input.GetAxis use that, and write a method which would take a Vector2 direction and instantiate a bullet towards that direction.
private void Update()
{
currentTime += Time.deltaTime;
Shooting();
}
private void Shooting()
{
var x = Input.GetAxis("Horizontal");
var y = Input.GetAxis("Vertical");
Vector2 direction = new Vector2(x, y);
}
private void Shoot(Vector2 direction)
{
// Instantiate bullets and fire them towards direction.
}
Google Input.GetAxis method, and Vector2 in case you do not know that.
Related
I've been having trouble with Quaternion lerps. I'm simply looking to rotate my character 90 degrees based on their current rotation. The script below executes that almost perfectly, except for the fact that my character rotates a full 90 degrees long before rotationTime reaches the max value of 1. For some reason, the value of rotationTime is not properly synced with the progress of the lerp, and I can't seem to figure out why. What am I missing?
public class Movement : MonoBehaviour
{
bool Rotating = false;
Quaternion targetRotation;
public float rotationTime = 0f;
public float speed = 0.1F;
private Rigidbody rb;
private void Awake(){
rb = GetComponent<Rigidbody>();
}
public void Turn(InputAction.CallbackContext context){ //executes when 'E' is pressed
if (context.started && Rotating == false) {
targetRotation = Quaternion.Euler(0,transform.eulerAngles.y + 90f,0);
rotationTime = 0;
Rotating = true;
}
}
void Update() {
if (Rotating == true) {
rotationTime = rotationTime + Time.deltaTime * speed;
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation,rotationTime);
};
if (rotationTime > 1) {
Rotating = false;
}
}
}
I suspect that the main issue here is that you are using transform.rotation in your Quaternion.Lerp. You change it every frame, so every frame the start rotation will be closer to the target rotation. You should add some kind of _initRotation variable, and set it to transform.rotation in your Turn method. I mean something like this:
public void Turn(InputAction.CallbackContext context)
{
if (context.started && Rotating == false)
{
targetRotation = Quaternion.Euler(0,transform.eulerAngles.y + 90f,0);
_initRotation = transform.rotation;
rotationTime = 0;
Rotating = true;
}
}
...
void Update()
{
...
transform.rotation = Quaternion.Lerp(_initRotation, targetRotation,rotationTime);
...
}
Also, you have a logical issue with the lerp function. It does not affect the result in your particular case, but it can cause problems later.
You increment your rotation time by Time.deltaTime * speed every frame, it is not correct as it is not time passed from the start of the rotation.
According to the Quaternion.Lerp documentation, t value is always clamped to [0, 1]. So it is more convenient to use normalized time value instead of abstract speed value (right now it has no physical sense, it is just a multiplier).
It would be much clearer to use something like this:
void Update()
{
...
rotationTime += Time.deltaTime;
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationTime / fullRotationTime);
...
}
Generally, I almost always work with carriers, and I recommend that you do the same.
public class Movement : MonoBehaviour
{
bool Rotating = false;
Vector3 targetRotation;
public float rotationTime = 0f;
public float speed = 0.1F;
private Rigidbody rb;
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
public void Turn(InputAction.CallbackContext context)
{ //executes when 'E' is pressed
if (context.started && Rotating == false)
{
targetRotation = new Vector3(0, transform.eulerAngles.y + 90f, 0);
rotationTime = 0;
Rotating = true;
}
}
void Update()
{
if (Rotating == true)
{
rotationTime = rotationTime + Time.deltaTime * speed;
transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, targetRotation, rotationTime);
};
if (rotationTime > 1)
{
Rotating = false;
}
}
}
Also consider using coroutines for more readable code and to improve performance.
Good work!
As it was mentioned already by this answer one of your main issues is that every time you use a new transform.rotation as interpolation start point
=> Your rotation starts fast and then gets slower and slower the closer you reach the target value.
There other issues here though:
You are using the transform.rotation.eulerAngles! From the API:
When using the .eulerAngles property to set a rotation, it is important to understand that although you are providing X, Y, and Z rotation values to describe your rotation, those values are not stored in the rotation. Instead, the X, Y & Z values are converted to the Quaternion's internal format.
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.
To avoid these kinds of problems, the recommended way to work with rotations is to avoid relying on consistent results when reading .eulerAngles particularly when attempting to gradually increment a rotation to produce animation. For better ways to achieve this, see the Quaternion * operator.
And then in general, since I see there is a Rigibody involved you shouldn't set or read anything directly via the Transform component at all but rather only go through that rigidbody! Otherwise you might break/fight against the physics resulting in strange behavior and breaking collision detection.
In your case in my eyes it is way easier to control the entire thing in a Corouine which avoids the need for all the class fields and imho is way easier to understand, control and maintain:
public class Movement : MonoBehaviour
{
public float speed = 0.1F;
[SerializeField] private Rigidbody rb;
private bool isRotating = false;
private void Awake()
{
if(!rb) rb = GetComponent<Rigidbody>();
}
public void Turn(InputAction.CallbackContext context)
{
//executes when 'E' is pressed
if (context.started && !isRotating)
{
StartCoroutine(RotateRoutine());
}
}
private IEnumerator RotateRoutine()
{
// little safety check first
if(Mathf.Approximately(speed, 0f)
{
yield break;
}
// Just to be really sure you avoid any concurrent routines
if (isRotating)
{
yield break;
}
// Lock so no other routine ca be started
isRotating = true;
// wait until we are in the next physics frame
yield return new WaitForFixedUpdate();
// store the initial rotation -> go throug the rigibody not the transform
var start = rb.rotation;
var end = start * Quaternion.Euler(0, 90f, 0);
var duration = 1 / speed;
for (var rotationTime = 0f; rotationTime < duration; rotationTime += Time.deltaTime)
{
// this would be a linear growing factor from 0 to 1
var factor = rotationTime / duration;
// optionally you could add ease in and out at the ends
// basically you can add whatever curve function you like to grow from 0 to 1 within the given rotationTime
factor = Mathf.SmoothStep(0, 1, rotationTime);
// interpolate from start to end while the factor grows from 0 to 1
var rotation = Quaternion.Slerp(start,end, factor);
// again for rigibdoy rather do this instead of going through transform
rb.MoveRotation();
// again wait for the next physics update
yield return new WaitForFixedUpdate();
}
// Just to be sure to end on clean values
rb.MoveRotation(end);
// release the lock for the next routine to start
isRotating = false;
}
}
I am very new to c# and I've come across a problem with my enemy spawner. I am making an endless top-down shooter, and after 20 enemies have been spawned and then destroyed, the enemies stop appearing. My score counter continues to increase though, which leads me to believe they're somehow being destroyed.
Here is the enemy spawn controller:
void Update()
{
if (!spawningObject && GameController.EnemyCount < spawnSettings[0].maxObjects)
{
spawningObject = true;
float pick = Random.value * totalWeight;
int chosenIndex = 0;
float cumulativeWeight = enemySpawnables[0].weight;
while(pick > cumulativeWeight && chosenIndex < enemySpawnables.Count - 1)
{
chosenIndex++;
cumulativeWeight += enemySpawnables[chosenIndex].weight;
}
StartCoroutine(SpawnObject(enemySpawnables[chosenIndex].type, Random.Range(spawnSettings[0].minWait / GameController.DifficultyMultiplier, spawnSettings[0].maxWait / GameController.DifficultyMultiplier)));
Spawn();
}
}
private IEnumerator SpawnObject(string type, float time)
{
yield return new WaitForSeconds(time);
randomSpawnPoint = Random.Range(0, enemySpawners.Length);
randomEnemy = Random.Range(0, enemy.Length);
enemyPool.SpawnObject(enemySpawners[randomSpawnPoint].position, transform.rotation);
spawningObject = false;
GameController.EnemyCount++;
}
And here is the enemy controller (attached to enemy prefab):
public void Start()
{
myRB = GetComponent<Rigidbody>();
player = FindObjectOfType<PlayerController>();
}
private void OnEnable()
{
player = FindObjectOfType<PlayerController>();
}
void Update()
{
if (health <= 0)
{
Die();
}
}
void FixedUpdate()
{
transform.LookAt(player.transform.position);
myRB.velocity = (transform.forward * moveSpeed);
//Falling
if(myRB.velocity.y < 0)
{
myRB.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
}
}
public void Die()
{
print("Enemy" + this.gameObject.name + " has died!");
EnemySpawn.instance.enemyPool.ReturnObject(this.gameObject);
player.GetComponent<PlayerController>().points += pointsToGive;
ScoreController.scoreValue += 1;
}
The only thing that should affect enemy health is the bullet. Here is the part of the bullet controller that affects enemy health:
public void OnTriggerEnter(Collider other)
{
if(other.tag == "Enemy")
{
triggeringEnemy = other.gameObject;
triggeringEnemy.GetComponent<EnemyController>().health -= damage;
PlayerController.instance.bulletPool.ReturnObject(gameObject);
}
}
The issue always happens after 20 enemies have been spawned/destroyed and 20 points have been achieved. Before then, enemy movement is normal and everything seems to be working as it should. It could very well be something very simple that I'm missing, but I'd appreciate any help!
When you do engage logic mistake good way to find it is Visual Studio debugging.
Here is good official article about whole process - https://docs.unity3d.com/Manual/ManagedCodeDebugging.html.
I would place breakpoint right at start of Update method to see that EnemyCount increases over time and then you will understand that you forgot to do decrement it.
I'm developing a game in Unity at the moment and I ran into some trouble. For the past 2 days I've been trying to get a slowdown powerup to work but trough various means but it everything that I tried doesn't seem to work.
I changed the code to the corroutine sugested since this already solved allot of problems and narrow down the issue:
using UnityEngine;
using System.Collections;
public class ClockCollision : MonoBehaviour
{
public void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("White Ball"))
{
StartCoroutine(SlowMoveSpeed());
//this.gameObject.SetActive(false);
Debug.Log("White Ball Sensed");
}
}
public IEnumerator SlowMoveSpeed()
{
Debug.Log("White Ball Sensed Twice");
InteractControl.moveSpeed = 2f; // set slow speed
yield return new WaitForSeconds(5); // suspend the process for 5 seconds
InteractControl.moveSpeed = 10f; // 5 seconds later, set speed back
}
}
InteractControl script. I just found the reason why it doesn't seem to react to the corroutine calls:
public class InteractControl : MonoBehaviour, IPooledObject
{
private Rigidbody2D rb;
GameObject target;
Vector3 directionToTarget;
public static int LevelStart = 0;
//public GameObject[] Balls;
Renderer m_Renderer;
public static float moveSpeed = 5f;
public void OnObjectSpawn()
{
if (ScoreScript.scoreValue > 4 && LevelStart == 0) //If statement is causing the powerup from not generating non powerup activation problem. It is above the InteractControl.moveSpeed call in the hierarchy
{
moveSpeed = 10f;
}
//m_Renderer = GetComponent<Renderer>();
target = GameObject.FindWithTag("White Ball");
rb = GetComponent<Rigidbody2D>();
//Movement speed of all the obstacles and powerups
MoveInteract(moveSpeed); //Method responsable for the movement of the obstacles and powerups, gets called at start
}
void MoveInteract(float moveSpeed) //Method responsable for the movement of the obstacles and stars
{
if (target != null)
{
if(ScoreScript.scoreValue > 4) //Determine when RedBall goes from going down in a straigh line to following white ball
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
// Debug.Log(getMoveSpeed());
}
else
{
directionToTarget = new Vector3(0, -1, 0);
rb.velocity = new Vector2(0, directionToTarget.y * moveSpeed);
}
}
else
{
rb.velocity = Vector3.zero;
}
}
}
Apparently the moveSpeed value in the if statement I just added takes presedence over the value changes I make to moveSpeed in my ClockCollision class.
Does anyone know how I can choose which value assignment of a static variable (in this case moveSpeed) takes presedence over the other
What should happen is that all the gameobjects that InteractControl is attached too should slow down to a velocity of the their rigidbody based on a moveSpeed value of 2f instead of the initial 10f and switch back to 10f after 5 seconds. However what actually happens is that they continue to have a the same velocity they had before the powerup was catched since the script Clocktime never gets enabled. The script itself does work(except for the fact that it doesn't switch back to 10f after 5 seconds) if update does manage to get executed however since I only want update to be executed in certain situations I need to be able to enable and disable it.
As you can see from the images below unless I missed something all my gameobjects and scripts are assigned and attached correctly in order for enabled to work:
Does anyone know how I could solve this problem?
Thanks in advance
As is stated in the comment above Update(), it is executed once every frame. So you create a new StopWatch every frame, that never has the chance to go up to 5 seconds because you check its elapsed time immediately after creating it. You should alter your code to only create a Stopwatch once, but check its elapsed time every Update. That will solve at least one of your problems.
frankhermes is correct, this code
InteractControl.LevelStart++;
var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
should go in Start().
However, I'd highly recommend using a Coroutine instead of the stopwatch. It's easier and more efficient. Unless I'm missing something, there shouldn't be an issue with having this all in one script. You'd do something like this:
public void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("White Ball"))
{
StartCoroutine(SlowMoveSpeed());
this.gameObject.SetActive(false);
}
}
IEnumerator SlowMoveSpeed() {
InteractControl.moveSpeed = 2f; // set slow speed
yield return new WaitForSeconds(5); // suspend the process for 5 seconds
InteractControl.moveSpeed = 10f; // 5 seconds later, set speed back
}
EDIT
The reason why it is still not working seems to be with this function:
void MoveInteract(float moveSpeed)
{
if (target != null)
{
if(ScoreScript.scoreValue > 4)
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
}
else
{
directionToTarget = new Vector3(0, -1, 0);
rb.velocity = new Vector2(0, directionToTarget.y * moveSpeed);
}
}
else
{
rb.velocity = Vector3.zero;
}
}
This is called once at the beginning, setting the velocity for the rigidbody. However, when the moveSpeed is updated in the coroutine, this function is not called again.
I haven't dealt much with static functions, but you could try to create a public static void function that passes in moveSpeed and re-sets the rb.velocity to that move speed. You could call this function from the Coroutine.
Another option that would be much less efficient would be to update the rigidbody velocity every frame to equal the moveSpeed.
Personally, I'd probably use delegates and events. You might want to try this if a static method doesn't work.
http://www.unitygeek.com/delegates-events-unity/
I would create an event in ClockCollision, something like
public delegate void OnSpeedChange(float speed);
public static event OnSpeedChange onSpeedChangeDelegate;
You'd also have to call the delegate in the coroutine:
if(onSpeedChangeDelegate != null)
{
onSpeedChangeDelegate(2f); // This invokes the delegate
yield return new WaitForSeconds(5); // suspend the process for 5 seconds
onSpeedChangeDelegate(10f); // 5 seconds later, set speed back
}
Then in InteractControl I'd subscribe to the delegate like this:
void Awake(){
ClockCollision.onSpeedChangeDelegate += UpdateSpeed;
}
//Don't forget to unsubscribe:
void OnDestroy(){
ClockCollision.onSpeedChangeDelegate -= UpdateSpeed;
}
// Update the speed
void UpdateSpeed(moveSpeed)
{
if(ScoreScript.scoreValue > 4)
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
}
else
{
directionToTarget = new Vector3(0, -1, 0);
rb.velocity = new Vector2(0, directionToTarget.y * moveSpeed);
}
}
DISCLAIMER: I can't guarantee that this code is error-free since I haven't tested it.
I'm coding my games boss behaviour and in the final stage of the battle the boss is supposed to charge towards the player and then move back to its original position. Wait for 5 seconds and then do the same.
I tried to achieve this using coroutines and Vector2.MoveTowards() but am not getting the desired effect, first off the boss does not "Move Towards" the player but instantly appears at the targetPosition and then just stays there, does not move back.
Below is my code:
private Vector2 chargeTarget;
private Vector2 tankStartPosition;
void Start()
{
chargeTarget = new Vector2(-5.0f, transform.position.y);
tankStartPosition = transform.position;
}
void Update()
{
if (Time.time > nextCharge)
{
StartCoroutine(TankCharge());
nextCharge = Time.time + chargeRate;
}
}
IEnumerator TankCharge()
{
transform.position = Vector2.MoveTowards(tankStartPosition, chargeTarget, Time.deltaTime * chargeSpeed);
transform.position = Vector2.MoveTowards(chargeTarget, tankStartPosition, Time.deltaTime * returnSpeed);
}
Any idea what I am doing wrong here? And how to get my desired action?
Thank you
Calling MoveTowards once only moves the game object once during that iteration of the game loop. Calling MoveTowards once doesn't move the game object all the way to its target (unless the maxDistanceDelta parameter is big enough to move the game object to its target in one iteration).
If the boss is instantly appearing at the target, I'm guessing your chargeSpeed is too big.
What you want to do is call MoveTowards once per Update cycle. However, the way you're doing your coroutine, the coroutine will only move the game object once and then exit. Normally coroutines will have a loop within them (otherwise the coroutine will exit after running once). Something like this:
IEnumerator TankCharge()
{
while (Vector3.Distance(transform.position, chargeTarget.position) > Mathf.Epsilon)
{
// Adjust this so this game object doesn't move the entire
// distance in one iteration
float distanceToMove = Time.deltaTime * chargeSpeed;
transform.position = Vector3.MoveTowards(transform.position, chargeTarget.position, distanceToMove)
yield return null;
}
}
However, for your situation, you don't really need a coroutine. You can just do this directly in Update()
private bool returnToStart = false;
private float timer;
void Update
{
float distanceToMove = Time.deltaTime * chargeSpeed;
if (timer <= 0)
{
if (!returnToStart)
{
transform.position = Vector3.MoveTowards(transform.position, chargeTarget.position, distanceToMove)
// Target reached? If so, start moving back to the original position
if (Vector3.Distance(transform.position, chargeTarget.position) <= Mathf.Epsilon)
{
returnToStart = true;
this.timer = this.chargeRate;
}
}
else
{
transform.position = Vector3.MoveTowards(transform.position, tankStartPosition.position, distanceToMove)
// Original position reached? If so, start moving to the target
if (Vector3.Distance(transform.position, tankStartPosition.position) <= Mathf.Epsilon)
{
returnToStart = false;
this.timer = this.chargeRate;
}
}
}
else
{
this.timer -= Time.time;
}
}
This question already has answers here:
Rotate GameObject over time
(2 answers)
Closed 5 years ago.
in my project i have a bridge at the moment when i colide with the bridge it rotates and don't stop rotate, what i want is to rotate that bridge trough a specific point and stop rotate, that bridge is inside a gameobject that is my pivotPoint in general i rotate the pivotPoint not the bridge, so i did this:
using UnityEngine;
using System.Collections;
public class fallBridge : MonoBehaviour {
private Rigidbody ball;
public GameObject bridgePivot;
private bool colided;
private bool rotating = true;
// Update is called once per frame
void Start(){
colided = false;
}
void Update () {
if (colided) {
if (rotating) {
Vector3 to = new Vector3 (0, 0, -85);
if (Vector3.Distance (bridgePivot.transform.eulerAngles, to) > 0.01f) {
bridgePivot.transform.eulerAngles = Vector3.Lerp (bridgePivot.transform.rotation.eulerAngles, to, Time.deltaTime);
} else {
bridgePivot.transform.eulerAngles = to;
rotating = false;
}
}
}
}
void OnCollisionEnter(Collision other)
{
ball = GameObject.FindWithTag ("Player").GetComponent<Rigidbody> ();
if (other.gameObject.tag == "Player" && ball.transform.localScale == new Vector3(2.0f,2.0f,2.0f)) {
Debug.Log("ENTER");
colided = true;
}
}
}
what am i doing wrong?? the colision detection works perfectly but on the update method it never stops rotate :S
You should do this with coroutine. Call the coroutine function and pass in the GameOjbect to rotate and the angle to rotate to when OnCollisionEnter is called. You can read how this function work here.
Te example below will rotate the GameObject -85 degree in z-axis within 3 seconds.
You can change that to whatever suits you.
public class fallBridge : MonoBehaviour
{
private Rigidbody ball;
public GameObject bridgePivot;
bool rotating = false;
void OnCollisionEnter(Collision other)
{
ball = GameObject.FindWithTag("Player").GetComponent<Rigidbody>();
if (other.gameObject.CompareTag("Player") && ball.transform.localScale == new Vector3(2.0f, 2.0f, 2.0f))
{
Debug.Log("ENTER");
Vector3 rotationAngle = new Vector3(0, 0, -85);
StartCoroutine(RotateObject(bridgePivot, rotationAngle, 3f));
}
}
IEnumerator RotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;
Vector3 currentRot = gameObjectToMove.transform.eulerAngles;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
The error is in this line:
bridgePivot.transform.eulerAngles = Vector3.Lerp (bridgePivot.transform.rotation.eulerAngles, to, Time.deltaTime);
When you use Lerp you typically have a fixed beginning and end, and you make the last parameter (t) go from 0 to 1. In your case the endpoint is fixed, the t parameters is fixed also and you vary your start. This means that each update you take a small step towards your goal, but this step is a percentage of the way. So each update you come closer, and your next step will be smaller.
What you should do is remember where you were when you started, and at what time you started. You also need to define a speed for your bridge. Then you can move the bridge from start to end and you get control over the speed.
public class fallBridge : MonoBehaviour {
private Rigidbody ball;
public GameObject bridgePivot;
public float rotateDuration = 2; // Time in seconds
// When you begin rotating you fill these three variables
private float startTime;
private Vector3 from;
private bool rotating = false;
// If the target doesn't change you might as well define it beforehand
private Vector3 to = new Vector3 (0, 0, -85);
void Update () {
if (rotating) {
float t = (Time.time - startTime) / rotateDuration;
if (t < 1) {
bridgePivot.transform.eulerAngles = Vector3.Lerp (from, to, t);
} else {
bridgePivot.transform.eulerAngles = to;
rotating = false;
}
}
}
void OnCollisionEnter(Collision other)
{
ball = GameObject.FindWithTag ("Player").GetComponent<Rigidbody> ();
if (other.gameObject.tag == "Player" && ball.transform.localScale == new Vector3(2.0f,2.0f,2.0f)) {
Debug.Log("ENTER");
startTime = Time.time; // Begin now
from = bridgePivot.transform.eulerAngles; // Remember the start position
rotating = true; // Signal to start rotating
}
}
}