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.
Related
i have a gameobject that goes up if it stays in collision for 5 Secs, the problem is it only work once , i tried calling the OnGUI in the update when ever the ToggleGUI = true but did't work
public float elapsedTime = 0f;
bool ToggleGUI = false;
bool isCreated = false;
Vector3 firstpos;
private void Update()
{
if(ToggleGUI == true)
{
OnGUI();
}
}
void OnTriggerStay(Collider other)
{
elapsedTime += Time.deltaTime;
if (elapsedTime >= 5.0f)
{
ToggleGUI = true;
}
}
void OnTriggerExit(Collider other)
{
elapsedTime = 0f;
}
void OnGUI()
{
if (ToggleGUI == true)
{
if (!isCreated)
{
firstpos = transform.position;
firstpos.y += 2f;
transform.position = firstpos;
isCreated = true;
}
}
}
after isCreated is set to true there is no place in your code where that variable is set again to false, therefore it can get inside the condition that moves the gameobject just once for the lifecycle of the script.
FYI when you pass a bool to an if you don't need to write ==true to have it execute when that variable is true, the name of the variable is enough
Your code seems a bit to complex for what you are trying to achieve.
If following your description what you want is just that the object goes up by 2f units every time you have stayed within a collider for 5 seconds I would simply use a Corouinte like e.g.
// The currently running Coroutine
private Coroutine _routine;
private int currentTriggers;
private void OnTriggerEnter(Collider other)
{
currentTriggers++;
// Start a routine if none is running already
if(_routine == null)
{
_routine = StartCorouine (Routine());
}
}
private void OnTriggerExit(Collider other)
{
currentTriggers--;
// If there are still other triggers do nothing
if(currentTriggers > 0) return;
// Cancel the routine if one is running
if(_routine != null)
{
StopCoroutine(_routine);
_routine = null;
}
}
IEnumerator Routine()
{
// As the name says wait 5 seconds
yield return new WaitForSeconds (5f);
// This is only reached if the routine wasn't stopped before the time passed
// Then move your object up
transform.position += Vector3.up * 2f;
_routine = null;
}
In general be very careful with your method names!
OnGUI is a special built-in event message that is called for handling events such as Keyboard or mouse input events, Repaint calls from the UI, etc. It might get called multiple times within a single frame and is absolutely not what you want to do here and for sure you should not call this method "manually" from Update.
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(...);
}
}
I am working on a 2D game in Unity. The game is limited to 60 seconds. I would like to have a time bomb which causes time reduction when the player hit the bomb.
In my script, I have a boolean which is named as "hitDetect" and I use a Coroutine() for the countdown.
I tried to push the bomb to the right side when the player hit the bomb and then check whether the collision takes place with these codes:
void OnCollisionEnter2D(Collision2D bombcol)
{
if(bombcol.gameObject.tag == "Enemy")
{
bombcol.gameObject.GetComponent<Rigidbody2D>().AddForce(transform.right * hitForce);
}
hitDetect = true;
}
This is my Coroutine() function which provides me to have a game which is successfully limited to 60 seconds except for the time penalty:
IEnumerator LoseTime()
{
while (true) {
yield return new WaitForSeconds (1);
timeLeft--;
if (hitDetect == true)
{
timeLeft= timeLeft - 5;
}
}
}
I also set the "hitDetect" as false in the start body.
void Start ()
{
StartCoroutine("LoseTime");
hitDetect = false;
}
However, these methods don't lead me to success. When the player hit the bomb, time penalty doesn't work. Where is my mistake? What would you recommend?
I would recommend to calculate the time in the Update() function. So you can be sure the hitDetect is observed every frame and the penalty is set only once if you reset hitDetect after subtracting the penalty.
public bool hitDetect = false;
public float timeLeft = 60.0f;
public float penaltyTime = 5.0f;
void Start(){
timeLeft = 60.0f;
hitDetect = false;
}
void Update(){
timeLeft -= Time.deltaTime;
if(hitDetect){
timeLeft -= penaltyTime;
// reset the hit!
hitDetect = false;
}
if(timeLeft < 0.0f){
// end game?
}
}
With this code your time is subtracted once by the penalty value if hitDetect is set true by your collision.
Hope this helps!
I need to make an object flicker every X seconds. So far I have the code for the actual flickering and it works great, however I need the flicker to come on for X seconds, then turning off, then come on for X seconds. Similar to turning a strobe light on (it flickers), then off.
I know something like invokeRepeating would work however the flickering relies on being in the FixedUpdate for it to run every frame.
For anyone wondering I'm actually trying to do something with image modulation and attention. Here is what I have so far:
public class scrFlicker : MonoBehaviour {
public SpriteRenderer sRen;
public float cycleHz; // Hz, the mesurement of cycles.
private float dtime = 0; // delta time
private Color alpha;
// Use this for initialization
void Start () {
sRen = GetComponent<SpriteRenderer>();
GetComponent<SpriteRenderer>().enabled = false;
alpha = sRen.color;
alpha.a = 0.4f;
sRen.color = alpha;
}
// Update is called once per frame
void FixedUpdate () {
startFlicker();
}
void startFlicker() {
dtime += Time.deltaTime;
float wave = Mathf.Sin((dtime * 2.0f * Mathf.PI) * cycleHz);
if(wave > 0.0f) {
GetComponent<SpriteRenderer>().enabled = true;
} else {
GetComponent<SpriteRenderer>().enabled = false;
}
if (wave == 0.0f) {
dtime = 0.0f;
}
}
}
You can create something like a timer below to manage the on and off time:
float toggletime;
void FixedUpdate () {
toggletime += Time.deltaTime;
if (toggletime < 2) // flicker will occur from 0-2 seconds
startFlicker();
else if (toggletime > 4) // nothing will occur between 2-4 seconds
toggletime = 0; // reset timer after 4 seconds have passed
}
"I know something like invokeRepeating would work however the flickering relies on being in the FixedUpdate for it to run every frame."
FixedUpdate is for Physics. Sure you can use it for other purposes but if they not physics related then it is not the primary purpose.
Invoke would actually do just fine, you have full control of it.
float timer = 2f;
bool isOn = false;
void Start()
{
Invoke("Method", timer);
}
void Method()
{
// you can change timer if needed
this.timer = Random.Range(0f, maxTimer);
this.isOn = !this.isOn;
Invoke("Method", this.timer);
}
void CancelMethodAndResetTimerForAnyReason()
{
CancelInvoke();
this.timer = Random.Range(0f, maxTimer);
Invoke("Method", this.timer);
}
I want to recreate the Tanks! game from the Unity tutorial series as multiplayer. I already watched the Network series for that as well. But I have a problem with implementing the shooting right. The tanks can charge up to increase the launch force of the bullet. It works for the host but the client gets stuck in firing.
The code:
[ClientCallback]
private void Update()
{
if (!isLocalPlayer)
return;
if (m_CurrentLaunchForce >= MaxLaunchForce && !m_Fired)
{
m_CurrentLaunchForce = MaxLaunchForce;
Debug.Log("Max force achieved! Firing!");
CmdFire();
}
else if (Input.GetButtonDown("Fire1"))
{
m_Fired = false;
m_CurrentLaunchForce = MinLaunchForce;
Debug.Log("Start charging up!");
}
else if (Input.GetButton("Fire1") && !m_Fired)
{
m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;
Debug.Log("Charging up!");
}
else if (Input.GetButtonUp("Fire1") && !m_Fired)
{
Debug.Log("Firing with low force!");
CmdFire();
}
}
[Command]
private void CmdFire()
{
m_Fired = true;
Rigidbody shellInstance = Instantiate(ShellPrefab, FireTransform.position, FireTransform.rotation);
shellInstance.velocity = m_CurrentLaunchForce * transform.forward;
m_CurrentLaunchForce = MinLaunchForce;
NetworkServer.Spawn(shellInstance.gameObject);
m_Fired = false;
}
The client which is not the host gets stuck in the second if case :
if (m_CurrentLaunchForce >= MaxLaunchForce && !m_Fired)
I checked the variables in the Debugger, and the currentLaunchForce gets never resetted to minLaunchForce.
I found the solution by myself. I implemented another function "fire()" which is called from the Update function first, which then again calls the command "cmdfire()". In the fire command I reset the variables and in the command I just tell the server to spawn the projectile for alle clients with the force as parameter.
private void fire()
{
m_Fired = true;
CmdFire(m_CurrentLaunchForce);
m_CurrentLaunchForce = MinLaunchForce;
m_Fired = false;
startReloadTime();
}
private void CmdFire(float launchForce)
{
Rigidbody bulletInstance = Instantiate(BulletPrefab, FireTransform.position, FireTransform.rotation);
bulletInstance.velocity = launchForce * transform.forward;
NetworkServer.Spawn(bulletInstance.gameObject);
}
It seems that if you use certain variables to check certain conditions on the client, you also have to reset them on the client himself, like in my case:
private float m_CurrentLaunchForce;
private bool m_Fired;
And then just tell the server to spawn your objects with options like force, position, rotation as function parameters to verify them, depending on how concerned you are about cheating.