I'm trying to make my first game, but I have a problem with the speed control:
Whenever I hold down on the button, everything slows down. I'm using time.deltatime which I know is global but I can't find any fixes.
void Start ()
{
self = GetComponent<Rigidbody2D>();
InvokeRepeating("SwitchDirections", switchTime, switchTime * 2);
StartCoroutine(SpawnBallLoop());
StartCoroutine(Count());
}
IEnumerator SpawnBallLoop ()
{
while (gameOver == false)
{
yield return new WaitForSecondsRealtime(2f);
SpawnBall();
}
}
void SpawnBall ()
{
Instantiate(ball, new Vector3(Random.Range(-2.18f, 2.18f), 4.6f, 0f), Quaternion.identity);
}
void UpdateClock ()
{
seconds += 1;
clock.text = "Time: " + seconds;
}
void SwitchDirections ()
{
moveSpeed *= -1;
}
void FixedUpdate ()
{
self.velocity = new Vector2(moveSpeed, 0f);
if (Input.GetMouseButton(0))
{
Time.timeScale = 0.5f;
}
else
{
Time.timeScale = 1f;
}
}
IEnumerator Count ()
{
while (gameOver == false)
{
yield return new WaitForSecondsRealtime(1);
UpdateClock();
}
}
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.transform.tag == "Ball")
{
GetComponent<SpriteRenderer>().enabled = false;
transform.GetChild(0).gameObject.SetActive(true);
}
}
Time.timeScale = 0.5f;
TimeScale is the scale at which time passes. This can be used for slow motion effects.
I suggest you to read this : here
Time.timeScale will slow down every game object in your scene. If you want only one game object to slow down use reduce movespeed of that one gameobject.
Related
I have a problem with an enemy on Unity2D.
The enemy should run to the player and if it's in the attackRange then it has to attack the enemy with a bullet, but the enemy follows my Player.
But there is a problem:
The enemy doesn't shoot with the bullet.
I changed the code, so when I press a button the enemy attacks too.
It worked only with the button.
Here's my code for the enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class Enemy2 : MonoBehaviour
{
public Transform target;
public Transform firePoint;
public float speed = 200f;
public float nextWaypointDistance = 3f;
public float range = 10f; // the range at which the enemy will start moving towards the player
public float attackRange = 8f; // the range at which the enemy will attack the player
float distance;
public Transform enemyGFX;
private Transform player; // reference to the player's transform
public GameObject EnemyWeapon;
Path path;
int currentWaypoint = 0;
bool reachedEndOfPath = false;
Seeker seeker;
Rigidbody2D rb;
public Animator animator;
bool Stop = false;
public static float Enemy2Direction;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
animator = GetComponentInChildren<Animator>();
player = GameObject.FindGameObjectWithTag("Player").transform;
InvokeRepeating("UpdatePath", 0f, .5f);
}
void UpdatePath()
{
if (seeker.IsDone())
{
seeker.StartPath(rb.position, target.position, OnPathComplete);
}
}
void OnPathComplete (Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
// Update is called once per frame
void FixedUpdate()
{
if (Pause.IsPause == false)
{
if (path == null)
{
return;
}
if (currentWaypoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
return;
}
else
{
reachedEndOfPath = false;
}
Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] -rb.position).normalized;
Vector2 force = direction * speed * Time.deltaTime;
rb.AddForce(force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWaypointDistance)
{
currentWaypoint++;
}
//You can make look it differently, if you delete 'rb.velocity' and add 'force' instead.
if (rb.velocity.x >= 0.01f)
{
enemyGFX.transform.localScale = new Vector3(-1f, 1f, 1f);
Enemy2Direction = 1f;
}
else if (rb.velocity.x <= -0.01f)
{
enemyGFX.transform.localScale = new Vector3(1f, 1f, 1f);
Enemy2Direction = 0f;
}
if (distance < attackRange)
{
if (Stop = false)
{
StartCoroutine(Attack());
}
}
}
distance = Vector2.Distance(transform.position, player.position);
if (distance > range)
{
Destroy(gameObject);
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
animator.SetBool("Damage", true);
}
if (collision.gameObject.tag == "Bullet")
{
animator.SetBool("Damage", true);
}
}
void Update()
{
if (animator.GetBool("Damage"))
{
StartCoroutine(DamageAnimation());
}
if (Input.GetKeyDown(KeyCode.L))
{
StartCoroutine(Attack());
}
}
IEnumerator Attack()
{
Debug.Log("Attacked");
Stop = true;
yield return new WaitForSeconds(2.0f);
animator.SetBool("Attack", true);
Instantiate(EnemyWeapon, firePoint.position, firePoint.rotation);
yield return new WaitForSeconds(2.0f);
animator.SetBool("Attack", false);
StartCoroutine(Wait());
}
IEnumerator DamageAnimation()
{
yield return new WaitForSeconds(2.0f);
animator.SetBool("Damage", false);
}
IEnumerator Wait()
{
yield return new WaitForSeconds(2.0f);
Stop = false;
}
}
I don't know what's wrong with my code.
Can somebody please help me?
if(Stop == false)
Replace the = in when you check the Stop bool with ==, in C# you use == in order to check for equality
See https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/equality-operators for more detail.
I'm working on a third person shooter and as the title states I'm having an issue having the particle effect for the muzzle flash play when I hold the left mouse button down. Even though I'm using Input.GetButton the particle effect only seems to play whenever I let go of the mouse. The basic shooting works correctly and I've tried using another particle effect which only led to the same issue so I believe the problem is somewhere within the code. This code is mostly from Brackeys' tutorial on Shooting with Raycasts if that narrows down the issue. I'd also appreciate alternatives to implementing a muzzle flash effect with the particle system.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class FullAuto : MonoBehaviour
{
public int damage = 10;
public float range = 100f;
public float impactForce = 30f;
public float fireRate = 15f;
public int maxAmmo = 10;
public int currentAmmo;
public float reloadTime = 1f;
private bool isReloading = false;
public Camera fpsCam;
public ParticleSystem muzzleFlash;
public GameObject impactEffect;
private float nextTimeToFire = 0f;
public Text ammoCount;
public Animator animator;
void Start()
{
currentAmmo = maxAmmo;
}
void OnEnable()
{
isReloading = false;
animator.SetBool("Reloading", false);
}
void Update()
{
if (isReloading)
return;
if (currentAmmo <= 0 || Input.GetKeyDown(KeyCode.R))
{
StartCoroutine(Reload());
return;
}
if (Input.GetButton("Fire1") && Time.time >= nextTimeToFire)
{
muzzleFlash.Play();
nextTimeToFire = Time.time + 1f / fireRate;
Shoot();
}
ammoCount.text = "Ammo: " + currentAmmo.ToString();
}
IEnumerator Reload()
{
isReloading = true;
Debug.Log("Reloading...");
animator.SetBool("Reloading", true);
yield return new WaitForSeconds(reloadTime - .25f);
animator.SetBool("Reloading", false);
yield return new WaitForSeconds(.25f);
currentAmmo = maxAmmo;
isReloading = false;
}
void Shoot()
{
currentAmmo--;
RaycastHit hit;
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Enemy enemy = hit.transform.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(damage);
}
if (hit.rigidbody != null)
{
hit.rigidbody.AddForce(-hit.normal * impactForce);
}
GameObject impactGO = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(impactGO, 2f);
}
}
}
Not sure but afaik Play has only an effect if the particle system was stopped/paused before → It doesn't actually restart the particle effect.
If the Particle System has been paused, then this resumes playing from the previous time.
If the Particle System has stopped, then the system starts from time 0, and, if it is relevant, the startDelay is applied.
I think you could rather use Emit and pass in how many particles to spawn each time
Emit count particles immediately
public float particleAmount = 20;
void Update()
{
if (isReloading)
return;
if (currentAmmo <= 0 || Input.GetKeyDown(KeyCode.R))
{
StartCoroutine(Reload());
return;
}
if (Input.GetButton("Fire1"))
{
nextTimeToFire -= Time.deltaTime;
if(nextTimeToFire <= 0)
{
muzzleFlash.Emit(particleAmount);
nextTimeToFire = 1f / fireRate;
Shoot();
}
}
ammoCount.text = "Ammo: " + currentAmmo.ToString();
}
IEnumerator Reload()
{
isReloading = true;
Debug.Log("Reloading...");
animator.SetBool("Reloading", true);
yield return new WaitForSeconds(reloadTime - .25f);
animator.SetBool("Reloading", false);
yield return new WaitForSeconds(.25f);
currentAmmo = maxAmmo;
isReloading = false;
nextTimeToFire = 0;
}
I am working on solo project that is horizontal 2D infinite runner. I have a problem in jumping mechanics where player can hold the jump button and jump as soon as he touches the ground. I want to force player to release the button to be able to jump again. I want to make same mechanics when he is floating(at the end of the jump when player starts falling down its y velocity is being reduced for few bits of second). I am following single responsibility principle so jumping and floating are 2 seperate scripts.
I have tried to implement a timer that will count while player is touching the ground and after some time the player would be able to jump again, but didn't manage to get asked result because you can hold the jump button all the time and after determined time spent on ground player would just jump again without releasing the button.
public class Jumping : MonoBehaviour
{
public bool isJumping;
public bool isGrounded;
public float speedUp;
public float verticalAxis;
public float timePassed;
public float jumpLimit;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0 && timePassed == 0)
{
isJumping = true;
}
}
private void FixedUpdate()
{
if (isJumping)
{
Jump();
}
}
private void OnCollisionStay2D(Collision2D collision)
{
if (collision.collider.tag == "Ground")
{
isGrounded = true;
timePassed = 0f;
}
}
private void Jump()
{
isGrounded = false;
//Modifying y component of players rigidbody(jumping)
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
//Counting time when jumping
timePassed += Time.deltaTime;
if (timePassed >= jumpLimit)
{
isJumping = false;
}
}
}
P.S. I have tried to shrink the code in this question as stackoverflow suggests but I didn't know what to cut out because according to me everything is relevant and crucial to solving the problem. Thanks!
without knowing the rest of your code I would use an additional flag and obviously something for getting the cool down delay like
public float jumpCoolDownTime;
private float coolDownTimer;
private bool canJump;
void Update()
{
// if no can jump you can't jump again
if(!canJump)
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
canJump = false;
isJumping = true;
coolDownTimer = 0;
}
}
else
{
// run timer to enable jump again
if(isGrounded) coolDownTimer += Time.deltaTime;
if(coolDownTimer >= jumpCoolDownTime)
{
canJump = true;
}
}
}
You could however simplify the code a bit using Coroutines
public bool isJumping;
public float speedUp;
public float verticalAxis;
public float jumpLimit;
public float coolDownTime;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
void Update()
{
// do nothing is already jumping
if(isJumping) return;
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
// start jumping
StartCoroutine(Jump());
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag != "Ground") return;
isGrounded = true;
StartCoroutine(CoolDown());
}
private IEnumerator Jump()
{
isJumping = true;
isGrounded = false;
coolDownTimer = 0;
var timePassed = 0f;
while(timePassed < jumpLimit)
{
// wait for the FixedUpdate call
yield return new WaitForFixedUpdate();
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
}
}
private IEnumerator CoolDown()
{
yield return new WaitForSeoncds(coolDownTime);
isJumping = false;
}
If you are using the axis to get the jump, you can see if the player have released the button by:
if (GetAxis("Vertical") == 0.0) { //Button up
// do stuff
}
if(GetAxis("Vertical") > 0)
{
//Button down, is jumping
}
In Unity axis aren't booleans, they are coefficients, if you play with a controller you can set different behaviors for different intensities.
In my code, I am calling a Slow motion function when the player enter to a Trigger, but I want this function to stop after 1 second, I tried the code below but the slow motion didn't work. Do you have any idea?
Player Script:
public Rigidbody Ball;
public float Speed = 50f;
public TimeManager timeManager;
bool SlowOn;
bool ClickDone = false;
// Use this for initialization
void Start () {
StartCoroutine(SlowOff());
}
// Update is called once per frame
void FixedUpdate () {
if (!ClickDone){
if (Input.GetMouseButton (0)) {
ClickDone = true;
Ball.velocity = transform.forward * Speed;
}
}
}
private void OnTriggerEnter (Collider other) {
if(other.gameObject.CompareTag ("SlowMotionArea")) {
if (SlowOn) {
timeManager.DoSlowMotion();
}
}
}
IEnumerator SlowOff () {
yield return new WaitForSeconds(2.0f);
SlowOn = false;
}
TimeManager Script:
public float SlowDownFactor = 0.05f;
public float SlowdownLength = 2f;
public void DoSlowMotion () {
Time.timeScale = SlowDownFactor;
Time.fixedDeltaTime = 0.02f * Time.timeScale ;
}
There are a few problems with your code
You never set SlowOn to true so your line
timeManager.DoSlowMotion();
is never executed. Somewhere in your code you should call
SlowOn = true;
I'm guessing but it seems that you wanted to use that bool to prevent multiple calls of timeManager.DoSlowMotion()? In this case it should rather be something like
if(!SlowOn)
{
timeManager.DoSlowMotion();
SlowOn = true;
}
Why do you call StartCoroutine(SlowOff()); in your Start() method?
I guess you should rather remove that line and place it after
timeManager.DoSlowMotion();
StartCoroutine(SlowOff());
so the Coroutine is started everytime a SlowMotion starts.
It is not enough for the SlowMotion to stop to just set your SlowOn = false.
In your TimeManager you should rather store the original TimeScale and reset them in a second method:
public float SlowDownFactor = 0.05f;
public float SlowdownLength = 2f;
private float originalTimeScale;
private float originalFixedDeltaTime;
public void DoSlowMotion () {
// before changing store current values
originalTimeScale = Time.timeScale;
originalFixedDeltaTime = Time.fixedDeltaTime;
Time.timeScale = SlowDownFactor;
Time.fixedDeltaTime = 0.02f * Time.timeScale ;
}
public void ResetTimeScales()
{
Time.timeScale = originalTimeScale;
Time.fixedDeltaTime = originalFixedDeltaTime;
}
and than you also have to call that ResetTimeScales method from the player
IEnumerator SlowOff () {
yield return new WaitForSeconds(2.0f);
timeManager.ResetTimeScales();
SlowOn = false;
}
I guess you know that
yield return new WaitForSeconds(2.0f);
will also be affected by the changed Timescale so your SlowMotion currently would be longer than the expected 2 seconds namely 2 / SlowDownFactor = 40!
You could avoid this by using the Time.unscaledDeltaTime as a countdown like
private IEnumerator SlowOff()
{
// since your title actually claims you want to reset after 1 second
float timer = 1;
while (timer > 0)
{
timer -= Time.unscaledDeltaTime;
yield return null;
}
timeManager.ResetTimeScales();
SlowDown = false;
}
I'm trying to create a 2D platform game in unity, when I try and make the character double jump, it won't work. I was wondering if i could get any help.
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public float maxSpeed = 3;
public float speed = 50f;
public float jumpPower = 150f;
public bool grounded;
public bool canDoubleJump;
private Rigidbody2D rb2d;
private Animator anim;
void Start ()
{
rb2d = gameObject.GetComponent<Rigidbody2D>();
anim = gameObject.GetComponent<Animator>();
}
void Update ()
{
anim.SetBool("Grounded", grounded);
anim.SetFloat("Speed", Mathf.Abs(rb2d.velocity.x));
if(Input.GetAxis("Horizontal") < -0.1f)
{
transform.localScale = new Vector3(-1, 1, 1);
}
if(Input.GetAxis("Horizontal") > 0.1f)
{
transform.localScale = new Vector3(1, 1, 1);
}
if(Input.GetButton("Jump"))
{
if(grounded)
{
rb2d.AddForce(Vector2.up * jumpPower);
canDoubleJump = true;
}
else
{
if (canDoubleJump)
{
canDoubleJump = false;
rb2d.velocity = new Vector2(rb2d.velocity.x, 0);
rb2d.AddForce(Vector2.up * jumpPower);
}
}
}
}
void FixedUpdate()
{
Vector3 easeVelocity = rb2d.velocity;
easeVelocity.y = rb2d.velocity.y;
easeVelocity.z = 0.0f;
easeVelocity.x *= 0.75f;
float h = Input.GetAxis("Horizontal");
//fake friction / easing x speed
if(grounded)
{
rb2d.velocity = easeVelocity;
}
//moving player
rb2d.AddForce((Vector2.right * speed) * h);
//limiting speed
if(rb2d.velocity.x > maxSpeed)
{
rb2d.velocity = new Vector2(maxSpeed, rb2d.velocity.y);
}
if(rb2d.velocity.x < -maxSpeed)
{
rb2d.velocity = new Vector2(-maxSpeed, rb2d.velocity.y);
}
}
}
The problem is that you're checking if the Jump button is currently in the down state. Pressing and releasing the button often takes place over multiple frames (i.e. Update() is called many times in the duration of button press).
There are two ways you can fix this problem.
The easiest (and probably best) is to make this alteration:
if(Input.GetButtonDown("Jump"))
GetButtonDown only returns true for the frame in which the button was first pressed, and false until it is released and pressed again.
The other is to include a second variable that prevents the activation of the second block until after the button is released. This is less ideal, but shows what's going on behind the scenes of GetButtonDown.
var isButtonDown = false;
Update() {
if(Input.GetButton("Jump"))
{
if(grounded)
{
rb2d.AddForce(Vector2.up * jumpPower);
canDoubleJump = true;
isButtonDown = true;
}
else if(!isButtonDown)
{
if (canDoubleJump)
{
canDoubleJump = false;
rb2d.velocity = new Vector2(rb2d.velocity.x, 0);
rb2d.AddForce(Vector2.up * jumpPower);
}
}
}
else {
isButtonDown = false;
}
}
Note that this does not address the "fall off a platform and jump once" that the double-jump ability typically includes. I'll leave that as an exercise to the reader.