(Unity) Rapidly click to speed up an object (otherwise decelerate) - unity3d

I’m trying to have a button that when you click it, cause the speed to increase incrementally for each time you click. When you stop clicking, it begins to slow down. I’ve tried various methods from rigidbody.velocity (didn’t work because object was kinematic), to transform.Translate, to transform.MovePosition. Any help would be appreciated, as I need to figure this out fast.
EDIT: The button being clicked is a sprite.
void OnMouseDown() {
clicked = true;
}
void ifClicked(){
if (clicked) {
speed += 0.5f;
gameObject.transform.Translate(Vector3.right * speed * Time.deltaTime);
StartCoroutine("Decelerate");
}
}
IEnumerator Decelerate() {
yield return new WaitForSeconds(1);
speed -= 0.5f;
}

You could have a float which will increase every time you click that button(like a multiplier) and the transform.position of that particular gameobject could be translated with that multiplier.
// A button could call this function from OnClick()
public void Multiply()
{
// where multiplier is a member variable of the class you're using this in
multiplier *= 1.1f;
}
// And add these into Update()
transform.position += movementVector * multiplier * Time.deltaTime;
multiplier *=0.95f;

Related

Input.GetKeyDown is won't work properly Unity 2D

I'm trying to make my square jump in Unity 2D when I press Space button. I have the following code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public float moveSpeed;
public float jumpSpeed;
float moveX;
Vector2 pos;
// Update is called once per frame
void Update()
{
MoveHorizontally();
Jump();
}
void MoveHorizontally(){
moveX = Input.GetAxis("Horizontal") * Time.deltaTime;
pos.x = moveX * moveSpeed;
transform.position = new Vector2(transform.position.x + pos.x,transform.position.y + pos.y);
}
void Jump(){
if (Input.GetKeyDown("space")){
GetComponent<Rigidbody2D>().AddForce(new Vector2(0,jumpSpeed * Time.deltaTime), ForceMode2D.Impulse);
Debug.Log("Jumped!");
}
}
}
When I press Space button, "Jumped!" message shows up, but my square not jumping. Any idea?
Thanks a lot.
I tried using Input.GetKey function. It works, but that function keeps making my square go upwards if i keep holding Space button.
U need to assing a value to jumpforce. and of course to the moveforce. if u dont wanna jump repeatly Just name a bool for checking if u are on ground. I use mine as" private bool = isGrounded".
and try the code below. It makes ur jump a condition as a function of touching ground.
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
isGrounded = true;
}
and here is my code for jump.
public void PlayerJump() {
if (Input.GetButtonDown("Jump") && isGrounded) {
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
whenever u jump it makes your bool variant false and whenever u touch ground it make ur bool true.
Make sure "Ground" must be same exactly the tag your ground has.
When you call AddForce() method you don't have to multiply with "Time.deltaTime".
What you can do is the following:
First cached your "Rigidbody2D" component from your Start() method because if you call it every frame it will affect your game performance.
private Rigidbody2D rigidbody2D;
void Start()
{
rigidbody2D = GetComponent<Rigidbody2D>();
}
Second, instead of Update() method you should call the FixedUpdate() method which is recommended when you deal with Physics
void FixedUpdate()
{
MoveHorizontally();
Jump();
}
Third, on your Jump() method as I said on top you should not multiply the jump force with Time.deltaTime, instead do the following:
public float jumpForce = 10f;
public void Jump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rigidbody2D.AddForce(Vector2.up*jumpForce);
}
}
You can adjust the jumpForce as you want.
Also Vector2.up is a shorthand for writing Vector2(0, 1)
You can read more about Vector2 here Vector2.up Unity's Documentation

Rotating an object around the same pivot gives different results

Rotating an object around the same pivot gives different results
So I'm trying to make a door that opens when clicked on, and I have a door that works good as is, but for some reason when I try to make it work for another door using RotateAround, it moves along the Z axis when closing. The code is identical to the other door that works and works when opening this door, so I have no clue as to why it's having trouble closing for this one. The code I'm using to rotate them is as follows
IEnumerator CloseDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.down, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
IEnumerator OpenDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.up, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
I'll start with some overall advices, regarding your code.
Don't use Time.deltaTime with new WaitForSeconds(...). Time.deltaTime is an amout of time, that passed between Update() calls. But your logic is not inside Update(). You use own time inervals with new WaitForSecons(...), but if the specified amout of time is less then Time.deltaTime, then it will be executed every Time.deltaTime after all Update() calls. It works good for you only because your time interval 0.001f is low enough to be executed every Update(). When your argument in new WaitForSeconts(...) becomes more them Time.deltaTime, the rotation speed of the door becomes too low to door be opened completely. To made your code more clear and safe, return null with yield instruction. In this case, you can be shure, that coroutine will be executed every Update(). More info here.
Your code works with transform positioning, and probably you have physics in your game. All changes with physical object supposed to be done in the FixedUpdate(). In your case your cant return new WaitForFixedUpdate() in yield instruction and use Time.fixedDeltaTime with it.
So speaking about main question. In your code, you are doing pretty unclear and unsafe thing, like hardcoding pivot with global position offset here:
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
Probably, not all doors will have same rotation, and for some of them offset, just with z coordinate will be wrong. Also it becomes wrong after door opening, because the position and rotation of the door changed when you rotating it around some point, that is not the center of the door. So you should base on local transform point, like this:
public class Door : MonoBehaviour
{
private bool _doorOpened = false;
private bool _doorOpening = false;
[SerializeField] // to see in the inspector.
private Vector3 _localDoorRotatePoint = new Vector3(0.5f, 0f, 0f);
// Update is called once per frame
void Update()
{
if (!_doorOpening && Input.GetKeyDown(KeyCode.E))
{
if (!_doorOpened)
{
StartCoroutine(OpenDoor());
}
else
{
StartCoroutine(CloseDoor());
}
}
}
IEnumerator CloseDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0). In your case it will be like (0, 0, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.down, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = false;
}
IEnumerator OpenDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0f). In your case it will be like (0f, 0f, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.up, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = true;
}
}
Helpfull links, that can help you with understanding this code:
Transform.TransformPoint
Transform.up

Unity3d lift movement down with keycode

this code does so when I press the A key or the B key, the elevator stops upwards on the first or second floor.
The question is
How do I modify the code that when the elevator is on the second floor, for example, and I press the A key again to make the elevator go down to the first floor?
Question two
If I replace
if (Input.GetKey (KeyCode.A)) {}
with this
if (Input.GetKeyUp (KeyCode.Keypad1)) {}
so the code does not work. Why?
Thank you for advice
Sorry for the bad English. Here is the code
public GameObject lift;
private bool keyHHit=false;
private bool keyHHitB=false;
void Update ()
{
if (Input.GetKey(KeyCode.B))
{
keyHHit=true;
}
if( keyHHit==true)
{
if(transform.localPosition.y >= 14.52)
{
transform.Translate(new Vector3(0, 0, 0) * 2 * Time.deltaTime, Space.Self);
}
else
{
transform.Translate(new Vector3(0, 2, 0) * 2 * Time.deltaTime, Space.Self);
}
}
if (Input.GetKey(KeyCode.A))
{
keyHHitB=true;
}
if( keyHHitB==true)
{
if(transform.localPosition.y >= 8.52)
{
transform.Translate(new Vector3(0, 0, 0) * 2 * Time.deltaTime, Space.Self);
}
else
{
transform.Translate(new Vector3(0, 2, 0) * 2 * Time.deltaTime, Space.Self);
//transform.Translate(Vector3.up * 0.05f);
}
}
}
So if I understand you correctly
you have two or 3 floors (doesn't matter, could be more later).
Once you press a button you want to move towards the target floor until you reach there. "Lock" input meanwhile
Once you reached a floor "unlock" Input again and allow to also go down if the current floor is above the target floor
I would use a Coroutine and Vector3.Lerp for this.
First lets have a class for floors so it is easier to add additional floors later
[Serializable]
public class FloorSetting
{
public KeyCode Key;
public Vector3 Position;
}
Then simply have an array of all floors target positions and according keys
// Configure these floors in the Inspector
// simply add the entries you need and adjust buttons and target positions
[SerializeField] private FloorSetting[] floors;
[SerializeField] private float moveUnitsPerSecond = 1f;
// This flag is for blocking any Input while moving
// thus preventing concurrent routines
private bool isMoving;
private void Update()
{
// if already moving do nothing
if(isMoving) return;
// check if any of the configured keys was pressed and start moving
// towards the according position
foreach(var floor in floors)
{
// You want to use GetKeyDown to only get the first press event
// you don't care about continues press since you will keep moving automatically until reaching the floor
if(Input.GetKeyDown(floor.Key))
{
StartCoroutine(MoveRoutine(floor.Position));
}
}
}
private IEnumerator MoveRoutine(Vector3 targetPosition)
{
// block concurrent routine
if(isMoving) yield break;
isMoving = true;
// Get the duration of movement
var startPosition = transform.position;
var duration = Vector3.Distance(startPosition, targetPosition) / moveUnitsPerSecond;
// Move smoothly towards the target position and add some easing
var timePassed = 0f;
while(timePassed <= duration)
{
// A interpolation factor between 0 and 1
var factor = timePassed / duration;
// optionally add ease-in and out
factor = Mathf.SmoothStep(0, 1, factor);
// Set the position to an interpolated position between start and target depending on the factor
transform.position = Vector3.Lerp(startPosition, targetPosition, factor);
// increase by the time passed since last frame
timePassed += Time.deltaTime;
// Tells Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
}
// Just to be sure in he end set it to hard position
transform.position = targetPosition;
// optionally add some cooldown in seconds
yield return new WaitForSeconds(1f);
// Release the lock so next move can start now
isMoving = false;
}
If you don't need the easing you could as well also implement it lot easier using Vector3.MoveTowards like e.g.
while(!Mathf.Approximately(Vector3.Distance(transform.position, targetPosition), 0)
{
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond);
yield return null;
}
transform.position = targetPosition;
Note: Typed on smartphone but I hope the idea gets clear

How to move a gameobject in Unity with the click on UI button when otherwise I would need the Update function to check for new inputs?

I am having a little trouble understanding the following. I am working on a simple game where the player can move a game object around by clicking left or right on the keyboard. Simple enough:
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
transform.position += Vector3.left * movementSpeed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.RightArrow))
{
transform.position += Vector3.right * movementSpeed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.UpArrow))
{
transform.position += Vector3.up * jumpHeight * Time.deltaTime;
}
if (Input.GetKey(KeyCode.DownArrow))
{
transform.position += Vector3.down * movementSpeed * Time.deltaTime;
}
}
However, I also have two buttons on screen, that are also supposed to move the player just as the keyevents do. What I dont understand is, that I need an update function to check every frame if a new button is pressed, right? So now I have my button and assign a script to it, where I can attach the button to a certain function of that script. But I cannot simply assign the button to a new "update" function that checks for different inputs of that button. How do I get my Ui Button to also controll the player JUST like the Update function does here?
Thank you!
Check out the Screenshot of the Scene. Just create an UI with an EventSystem. Create a button within your UI. Attach an EventTrigger to your button. Now create two Events on your EventTrigger:
(1) Pointer Down (will be called, when the button get's pressed down.)
(2) Pointer Up (will be called when the button is released.)
Attach the script at the end of this answer as a component to your player or cube or whatever you wanna move.
Now you can see on the screenshot, that the public method MoveCube.MoveLeft get's called whenever the MoveLeftButton is pressed. Whenever it is released, the MoveCube.StopMovingLeft gets called. The joke is about the bool, that will be switched from false to true and back. The actual movement happens in the Update, that essentially follows the same logic as the script you have provided. Hope this helps.
private bool moveLeft, moveRight;
void Start ()
{
// Get Rigidbody or CC or whatever you use to move your object.
}
void Update ()
{
if (moveLeft)
{
// Move Left
Debug.Log("Moving Left");
}
}
public void MoveLeft()
{
moveLeft = true;
}
public void StopMovingLeft()
{
moveLeft = false;
}
EDIT: Very important - I just saw that you are using transform.position += Vector3 to move the object. You will have big troubles with accurate collision, as your function is literally teleporting your gameobject to the new position. To avoid bad colliding you should use Rigidbody.AddForce or if you want to use transform you can easily use transform.Translate:
From Unity Docs:
transform.Translate(Vector3.forward * Time.deltaTime);
EDIT 2: Here is the code you have requested in the comment to this answer. Just copy the script, should work fine.
private bool moveLeft, moveRight;
// Create the rigidbody and the collider
private Rigidbody rb;
private Collider col;
// Create a variable you can change in inspector window for speed
[SerializeField]
private float speed;
void Start ()
{
// You can either use a CC (Character Controller) or a Rigidbody. Doesn't make that much of a difference.
// Important thing is, if you use a rigidbody, you will need a collider as well. Collider detect collision
// but rigidbodies do the actual work with regards to physics. In this case we use rigidbody/collider
// Get the rigidbody
// If you are making a 2D game, you should use Rigidbody2D
// The rigidbody will simulate actual physics. I tested this script, the
// rigibody will accelerate and will need time to slow down upon breaking
// You can change it's mass and stuff
rb = GetComponent<Rigidbody>();
// Now in this case we just get any collider. You can be more specific, if you know which collider your gameobjects has
// e.g. BoxCollider or SphereCollider
// If you are making a 2D game, you should use Collider2D
col = GetComponent<Collider>();
}
void Update ()
{
if (moveLeft)
{
// If you make a 2D game, use Vector2
rb.AddForce(Vector3.left * speed);
}
}
public void MoveLeft()
{
moveLeft = true;
}
public void StopMovingLeft()
{
moveLeft = false;
}
You could do something like this. In your script, attached to a button:
using UnityEngine.EventSystems; // Required when using Event data.
public class ExampleClass : MonoBehaviour, IPointerDownHandler // required interface when using the OnPointerDown method.
{
bool moveLeft = false;
// Do this when the mouse is clicked over the selectable object this script is attached to.
public void OnPointerDown(PointerEventData eventData)
{
moveLeft = true;
Debug.Log(this.gameObject.name + " was clicked.");
}
public void OnPointerUp(PointerEventData eventData)
{
moveLeft = false;
Debug.Log(this.gameObject.name + " was released.");
}
void Update()
{
if (moveLeft)
// Move left code
}
}
This is a fairly dumb example, but should be at least something you can expand on.

unity lerp scale doesn't work

I am trying to make an object scale from zero to it's normal size when I instantiate it, so it will look like it popped to the screen.
So when the object start I get it's normal size then update it to zero, and then in update I am scaling it.
This is my code:
void Start()
{
normalScale = transform.localScale;
transform.localScale *= 0.1f;
}
void Update()
{
transform.localScale = Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
// destroy item
if (transform.localScale == normalScale)
{
transform.localScale = transform.localScale * 0.1f;
}
}
Thank you.
With this you're always changing from it's current scale, which of course you changed last update
transform.localScale = Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
What you need to do is create two Vector3 outside of the update function, one for the start size, one for the final size
Vector3 start = Vector3.zero;
Vector3 end = new Vector3(1,1,1);
You'll also need a timer:
float lerpTime = 0;
Altogether you get
transform.localScale = Vector3.Lerp(start, end, lerpTime);
lerpTime += Time.deltaTime // times whatever multiplier you want for the speed
There are a couple of issues with your code that are likely to be causing problems. The first is the start/end values you're passing in to the lerp:
Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
On your second frame, .localScale is roughly (0.1, 0.1, 0.1). The max value of the lerp on your second frame is the value from your first frame. That means that your current code is endlessly shrinking the target - the opposite of what you wanted.
The other problem is the way you're handling the time. You're passing 5f * Time.deltaTime, which is (probably) always going to be less than 1. This means you will never reach the maximum value.
So, to fix these, you need two things: first, you need to make sure your min/max values are actually min/max values, not arbitrary values in between. Second, you need to make sure your third parameter progresses smoothly from 0 to 1 over a defined time.
Something like this:
public float ScaleTime = 5f; // the time it'll take to grow, settable in the inspector
public float ScaleTime = 5f; // the time it'll take to grow, settable in the inspector
Vector3 _targetScale;
Vector3 _startScale;
float _currentLerp = 0f;
void Start()
{
_targetScale = this.localScale;
_startScale = _targetScale * 0.1f;
}
void Update()
{
_currentLerp += Time.deltaTime * ScaleTime;
if (_currentLerp < 1)
{
transform.localScale = Vector3.Lerp(_startScale, _targetScale, _currentLerp);
}
else
{
transform.localScale = _targetScale; // make sure we definitely hit the target size
... do whatever else you need to do here...
}
}
As per https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html your 3rd parameter in
transform.localScale = Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
is supposed to indicate the "fracJourney", in other words it should change from 0f to 1f to indicate the progress of your animation, but Time.deltaTime will give you the time since the last frame so that you probably see it jump around 0.005 (or whatever your frame rate is).
You need to add another variable to indicate the progress of your animation:
public float speed = 1.0F;
private float startTime;
void Start() {
startTime = Time.time;
normalScale = transform.localScale;
}
void Update()
{
float distCovered = (Time.time - startTime) * speed;
transform.localScale = Vector3.Lerp(Vector3.zero, normalScale, distCovered);
// destroy item
if (transform.localScale >= normalScale)
{
startTime = Time.time; // Reset the animation
}
}