I'm looking for a way to apply a force to an object after one button press (using GetKeyDown), essentially toggling the force.
I've had trouble for a few days trying to work this out as I'm learning how to use C#. I'm trying to work out a slide or dash for a 2D platformer, akin to Megaman sliding. This is my code so far, however when I press G, it teleports me forward instead of giving me a set velocity over time (where I want the player to move steadily foward)
public float slideCount;
public float maxSlideCount;
public bool isSliding;
void Update () {
if (Input.GetKeyDown (KeyCode.G) && isSliding == false) {
slideCount += Time.deltaTime;
isSliding = true;
if (slideCount < maxSlideCount) {
rb2d.AddRelativeForce (Vector2.right * 0.05f, ForceMode2D.Impulse);
} else
slideCount = 0;
isSliding = false;
}
Appreciate it
So a standard toggle looks like:
void Update() { // User-interactions happen in the graphics-frame
if (inputDown) {
toggle = !toggle;
}
}
Then to use toggle:
void fixedUpdate() { // Because you're applying forces, do it in the physics-frame
if (toggle) {
myRigidBody.AddForce(...);
}
}
Related
I have a Canvas with an Image and a TextMeshPro (TMP) as a child, and the dialog's canvas component is set to false in the Start() method so as to hide it in the main Canvas. The TMP appears over the image (like a text inside a dialog box). I have a player and a coin sprite in a 2D environment. When the player picks up the coin, I try to display the dialog box and the TMP as shown below.
public class PlayerMovement : MonoBehaviour {
public GameObject suggestion;
public GameObject dialogBox;
private bool wasSuggestionShown; //to check if dialog was shown
private void Start()
{
wasSuggestionShown = false;
suggestionTimer = 0;
dialogBox.GetComponent<Canvas>().enabled = false; //To hide the dialog box
}
void Update () {
//horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed; // for pc keys
horizontalMove = CrossPlatformInputManager.GetAxis("Horizontal") * runSpeed; //for smartphone input
if (wasSuggestionShown)
{
suggestionTimer += Time.deltaTime;
if (suggestionTimer > 5)
{
wasSuggestionShown = false;
dialogBox.GetComponent<Canvas>().enabled = false; //TO hide dialog box after displaying it
}
}
}
void FixedUpdate ()
{
// Move the character
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
}
//For destroying coin object on collision with player
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("coin"))
{
//For destroying coin and to recude player movement speed.
Destroy(col.gameObject); //Coin Disappears
isRunSpeedReduced = true;
runSpeed = 10f;
//For Showing dialog box
dialogBox.GetComponent<Canvas>().enabled = true;
wasSuggestionShown = true;
}
}
}
In the OnTriggerEnter2D() method, I check if the player character touches the coin and if so, I destroy the object and display the dialog box and TMP and hide them after 5 seconds. The problem is that
"When I include another coin, the same dialog box and TMP, do not show up when the player picks the second coin. Both the coins have the same tag 'coin' "
One may argue that if the script is in the same object which was destroyed or is inactive, then it is impossible. But I am doing all of this in the Player's movement script which is attached to the player Object.
Also, the way that I toggle the dialog box does not make a difference. Be it dialogBox.GetComponent<Canvas>().enabled = true; or dialogBox.SetActive(true)
Either of these display only once and that is the first occurence.
Even if I want to instantiate it, I don't know the exact transforms to position it properly in the canvas. (I want it in the bottom middle part, like how it can be anchored)
Scene Hierarchy:
The issue is in your Update(), where you turn off the canvas after suggestionTimer ticks over:
void Update () {
//horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed; // for pc keys
horizontalMove = CrossPlatformInputManager.GetAxis("Horizontal") * runSpeed; //for smartphone input
if (wasSuggestionShown)
{
suggestionTimer += Time.deltaTime;
if (suggestionTimer > 5)
{
wasSuggestionShown = false;
dialogBox.GetComponent<Canvas>().enabled = false; //TO hide dialog box after displaying it
}
}
}
The cause is that you never reset the suggestionTimer when you hit a new coin. Do this:
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("coin"))
{
//For destroying coin and to recude player movement speed.
Destroy(col.gameObject); //Coin Disappears
isRunSpeedReduced = true;
runSpeed = 10f;
//For Showing dialog box
dialogBox.GetComponent<Canvas>().enabled = true;
wasSuggestionShown = true;
// !!! ADD THIS
suggestionTimer = 0;
}
}
The problem is that on OnTriggerEnter2D, you set wasSuggestionShown = true, and then in the very next Update method, you're checking wasSuggestionShown, seeing that it's true, and turning the canvas off again straight away.
This might help. It's just one, of a dozen ways, that could help:
private bool _showSuggestion = true;
public bool showSuggestion
{
get { return _showSuggestion; }
set
{
if ( !value && _showSuggestion )
{
dialogBox.SetActive ( false );
_showSuggestion = value;
}
}
}
void Update () {
//horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed; // for pc keys
horizontalMove = CrossPlatformInputManager.GetAxis("Horizontal") * runSpeed; //for smartphone input
if ( showSuggestion )
{
suggestionTimer += Time.deltaTime;
if ( suggestionTimer > 5f ) showSuggestion = false;
}
}
The code above will wait until the first time you set showSuggestion to false, and then after that, ignore any further attempt to turn the suggestions on. Which I suspect that's what you'd like? If I'm reading the situation wrong, the code can be jiggled a little to get the right behaviour.
I have a question. I have to make the character move in two positions, just like in the picture below.
All I want to know is how to do this thing. For example, character moves automatically to the right, then when it clicks up, it already moves to the top bar and still moves to the right, and already when it clicks down it to go back to the bar down.
public float speed;
public GameObject[] buttons;
private void Start()
{
foreach (GameObject button in buttons)
button.GetOrAddComponent<MouseEventSystem>().MouseEvent += ChangeBoyPosition;
}
private void ChangeBoyPosition(GameObject target, MouseEventType type)
{
if (type == MouseEventType.CLICK)
{
int buttonIndex = System.Array.IndexOf(buttons, target);
if (buttonIndex == 0)
{
//do down position
}
else
{
//do up position
}
}
}
void Update()
{
//used for automatic movement
//set a speed that responds to the speed of the boy
//automatically move it to the right, then we will change the direction when it reaches the X axis limit
transform.Translate(Vector3.right * speed * Time.deltaTime);
}
Basically I have two buttons, up and down, with these I manipulate the character. If anyone knows how I could make this physics simpler? Thank you!!!
You can try using Physics2D.gravity to modify the y value.
Physics2D.gravity = new Vector2(0, -1); // To go down
Physics2D.gravity = new Vector2(0, 1); // To go up
You'd probably be putting this in an update function since it needs to check inputs, so I would recommend creating two vectors gravityUp and gravityDown and setting Physics.gravity equal to one or the other, depending on which direction you want to go.
Like this:
Vector2 gravityUp = Vector2.up;
Vector2 gravityDown = Vector2.down;
The same would apply in 3D, except you'll have to use Vector3 and Physics.gravity.
Your code would possibly look like this:
Vector2 gravityUp = Vector2.up;
Vector2 gravityDown = Vector2.down;
private void ChangeBoyPosition(GameObject target, MouseEventType type)
{
if (type == MouseEventType.CLICK)
{
int buttonIndex = System.Array.IndexOf(buttons, target);
if (buttonIndex == 0)
{
Physics2D.gravity = gravityDown;
}
else
{
Physics2D.gravity = gravityUp;
}
}
}
I have a 2D shooting game on Unity using C# now and I want to increase the fire rate of the ship for 5 seconds when it gets a power up item. It kinda works but when the ship gets the power up, the fire rate does not change until the button is released and pressed again. Is there a way to change the fire rate as soon as it gets the power up even while the button is being pressed? Also, the power up function is something that I came up with and if there is a better way to make the power up functions, that will be very helpful too. Thanks in advance :)
void Update(){
if (Input.GetKeyDown(KeyCode.Space)){
InvokeRepeating("Fire", 0.000001f, fireRate);
}
}
void PowerUp()
{
Upgrade = true;
timeLeft = +5f;
if (Upgrade == true)
{
fireRate = 0.1f;
}
if (timeLeft <= 0)
{
Upgrade = false;
fireRate = 0.5f;
}
}
You should pass an reference type to the Fire coroutine instead of a float fireRate.
Just wrap fireRate in a class should work:
class FireData
{
public float fireRate = 0.1;
}
Then in your script,
FireData fireData = new FireData { fireRate = 0.5f };
void Update(){
if (Input.GetKeyDown(KeyCode.Space)){
InvokeRepeating("Fire", 0.000001f, fireData);
}
}
void PowerUp()
{
Upgrade = true;
timeLeft = +5f;
if (Upgrade == true)
{
fireData.fireRate = 0.1f;
}
if (timeLeft <= 0)
{
Upgrade = false;
fireData.fireRate = 0.5f;
}
}
In Fire() coroutine, use this fireData.fireRate to get the fireRate.
By the way, I think your power up functions is good enough.
But the way of using coroutine is not correct.Don't call InvokeRepeating on the same function multiple times.
if (Input.GetKeyDown(KeyCode.Space)){
InvokeRepeating("Fire", 0.000001f, fireRate);
}
Instead, you should use a bool value to control when the fire will start.
if (Input.GetKeyDown(KeyCode.Space)){
fireData.Firing = true;
}
if(fireData.Firing)
{
InvokeRepeating("Fire", 0.000001f, fireRate);
}
Also remember to add a logic to stop firing via StopCoroutine.
I am a bit of a noob to programming and I am trying to make a GameObject , deactivate and reactivate over a set amount of seconds.For example I want my star to slowly blink before it goes away, to create a cool looking effect. If there is a better way of using this method done with out using SetActive(false) and what not , please feel free to give me your method - This is my code , Sorry if its messy i gotta get better at this but i will in due time
Thanks guys
//Timers
public float ScoreTimer;
public float[] TimeMarkStamp;
//Scoring
public int totalCollStars;
[Space]
public int maxStars = 5;
public GameObject[] StarImages;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
ScoreTimer += Time.deltaTime;
if (ScoreTimer <= TimeMarkStamp[0])
{
Debug.Log("It works");
StarImages[0].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[1])
{
Debug.Log("It workds" + TimeMarkStamp[1]);
StarImages[1].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[2])
{
Debug.Log("It works" + TimeMarkStamp[2]);
StarImages[2].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[3])
{
//This is not working
InvokeRepeating("flickerEffect", 3f, 1f);
}
}
void flickerEffect()
{
bool flickCheck = false;
if (flickCheck == false)
{
StarImages[3].SetActive(true);
flickCheck = true;
}
else if (flickCheck == true)
{
StarImages[3].SetActive(false);
flickCheck = false;
}
}
}
If there is a better way of using this method done with out using
SetActive(false) and what not
Yes, there is a better way, other than using the SetActive function. You should change the alpha color of the GameObject from 0 to 1 back and forth. After that you can then disable the GameObject with SetActive. This saves how much garbage would have been generated when repeatedly calling the SetActive function.
If this is a 3D GameObject, change the Rendering Mode from Opaque(default) to Fade or Transparent.
A simple function that can do this:
void blink(GameObject obj, float blinkSpeed, float duration)
{
StartCoroutine(_blinkCOR(obj, blinkSpeed, duration));
}
IEnumerator _blinkCOR(GameObject obj, float blinkSpeed, float duration)
{
obj.SetActive(true);
Color defualtColor = obj.GetComponent<MeshRenderer>().material.color;
float counter = 0;
float innerCounter = 0;
bool visible = false;
while (counter < duration)
{
counter += Time.deltaTime;
innerCounter += Time.deltaTime;
//Toggle and reset if innerCounter > blinkSpeed
if (innerCounter > blinkSpeed)
{
visible = !visible;
innerCounter = 0f;
}
if (visible)
{
//Show
show(obj);
}
else
{
//Hide
hide(obj);
}
//Wait for a frame
yield return null;
}
//Done Blinking, Restore default color then Disable the GameObject
obj.GetComponent<MeshRenderer>().material.color = defualtColor;
obj.SetActive(false);
}
void show(GameObject obj)
{
Color currentColor = obj.GetComponent<MeshRenderer>().material.color;
currentColor.a = 1;
obj.GetComponent<MeshRenderer>().material.color = currentColor;
}
void hide(GameObject obj)
{
Color currentColor = obj.GetComponent<MeshRenderer>().material.color;
currentColor.a = 0;
obj.GetComponent<MeshRenderer>().material.color = currentColor;
}
Usage:
void Start()
{
blink(gameObject, 0.2f, 5f);
}
If this is a SpriteRender, you have to replace all the obj.GetComponent<MeshRenderer>().material.color code with obj.GetComponent<SpriteRenderer>().color.
I am looking for a help with make a delay in Unity in Update function.
I created something like this below. The cube is moving rotates once and then is waiting > rotates once > waiting ....
And there is my question. How i can make cube rotates constantly for some time instead of once. For Example: Wait 2sec, rotating constantly 5sec, Wait 2sec, rota....
I thinked about replace
ForCube.transform.Rotate (10, 10, 10);
by rotating Animation. But I want create it with transform.Rotate. Is there any option to do this?
using UnityEngine;
using System.Collections;
public class Ruch : MonoBehaviour
{
public float speed = 5;
public GameObject ForCube;
bool work = true;
// Use this for initializat
void Start ()
{
ForCube = GameObject.Find ("Cube");
Debug.Log (ForCube);
}
// Update is called once per frame
void Update ()
{
if (work) {
StartCoroutine (WaitSome ());
}
}
private IEnumerator WaitSome ()
{
work = false;
yield return new WaitForSeconds (3f);
ForCube.transform.Rotate (10, 10, 10);
work = true;
}
}
At the moment it looks like to me you are using a StartCoroutine which will work fine, but if you want maybe a little more control over when to rotate and when to stop you can use the Time.deltaTime The time in seconds it took to complete the last frame (Read Only).http://docs.unity3d.com/ScriptReference/Time-deltaTime.html
So basically you have yourself a float variable called Rotate which is lets say 10f
Then inside of your Update function
void Update ()
{
if(Rotate > 0)
{
Rotate -= Time.deltaTime;
ForCube.transform.Rotate (10, 10, 10);
}
}
Then when Rotate is equal to 0 it will stop, but then you can use your work bool to start a new timer.
One big think to take in is to use the Time.deltaTime, if you don't use this and you just use an int or whatever variable type the timer will differ depending on the FPS of the game for the player.
Let me know if you need anymore help :)
Instead of using coroutines, you can do it directly in the update function like this:
[SerializeField]
private float timeToWait; //In seconds
[SerializeField]
private float timeToRotate; //In seconds
private float timer = 0;
private bool waiting = true; //Set this to false if you want to rotate first, wait later
void Update()
{
if(!waiting) RotateYourObjectALittleBit(); //Call your own function or do whatever you want
timer += Time.deltaTime;
if(timer >= timeToWait && waiting) {
waiting = false;
timer = 0;
}
else if(timer >= timeToRotate && !waiting) {
waiting = true;
timer = 0;
}
}
This code is untested, so please let me know if you require further clarification or if it doesn't work.
Thanks everyone for fast Answer and help to solve my problem. I really appreciate that.
I created something like this:
Version 1.0
When the space key is down the cube start rotating for RotateTime, after this the Timer reset to start value(RotationTime), and u can click again button for rotate.
using UnityEngine;
using System.Collections;
public class Ruch : MonoBehaviour
{
public GameObject ForCube;
public float RotateTime = 5;
public float Timer = 0;
private bool Rotate = false;
// Use this for initializat
void Start ()
{
Timer = RotateTime;
ForCube = GameObject.Find ("Cube");
Debug.Log (ForCube);
}
// Update is called once per frame
void Update ()
{
//Start Rotating When Press Space Key
if (Input.GetKeyDown (KeyCode.Space)) Rotate = true;
else if (!(Input.GetKeyDown (KeyCode.Space))&&Timer <=0) Rotate = false;
RotateForSec (ref Timer);
}
//Function to Rotate for X sec
void RotateForSec(ref float sec)
{
if (Rotate && sec > 0) {
Debug.Log (Time.time);
ForCube.transform.Rotate (10, 10, 10);
sec -= Time.deltaTime;
}
//Reset Rotating Time after rotating
if (!Rotate && sec <= 0) Timer = RotateTime;
}
}
Version 2.0
The rotating of cube continues for 5 seconds and then automatically without pressing a key it wait some time and start over rotating. Again again and again ...
public GameObject ForCube;
public float RotateTime = 5;
public float Timer = 0;
public float PauseTime = 0;
private bool Pause = false;
private bool Rotate = true;
// Use this for initializat
void Start ()
{
Timer = RotateTime;
PauseTime = RotateTime;
ForCube = GameObject.Find ("Cube");
Debug.Log (ForCube);
}
// Update is called once per frame
void Update ()
{
//Start Rotating When Press Space Key
if (Rotate)
Pause = false;
else if (!Rotate) {
Pause = true;
}
if (!Pause)
RotateForSec (ref Timer);
else RotatePause ();
}
//Function to pause PauseTime sec
void RotatePause()
{
if (PauseTime > 0) {
PauseTime -= Time.deltaTime;
} else {
Pause = false;
Rotate = true;
PauseTime = RotateTime;
}
}
//Function to Rotate for X sec
void RotateForSec(ref float sec)
{
if (Rotate && sec > 0) {
Debug.Log (Time.time);
ForCube.transform.Rotate (10, 10, 10);
sec -= Time.deltaTime;
} else Rotate = false;
//Reset Rotating Time after rotating
if (!Rotate && sec <= 0) Timer = RotateTime;
}
}
Its working but what you think about that, is it done correctly or it is a bad way?