So I put the object in the scene and then I made it "invisible" (deactivate if you will) from the inspector (the checkmark box next to the object's name) and after waiting 8 seconds it doesn't become visible. I am using Unity 2d and C#.
I have the game start paused for three seconds then plays after that which works. The first script is that one. The item is supposed to reappear after 8 seconds so after the game resumes, which doesn't work.
//delay before level starts script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class countDown : MonoBehaviour
{
public GameObject CountDown;
private void Start()
{
StartCoroutine("StartDelay");
}
void Update()
{
}
IEnumerator StartDelay()
{
Time.timeScale = 0;
float pauseTime = Time.realtimeSinceStartup + 3f;
while (Time.realtimeSinceStartup < pauseTime)
yield return 0;
CountDown.gameObject.SetActive(false);
Time.timeScale = 1;
}
{
//script for the flower to appear
IEnumerator Start()
{
print(Time.time);
yield return new WaitForSeconds(8);
print(Time.time);
flowerInScene.gameObject.SetActive(true);
}
[SerializeField] Transform flowerInScene;
}
I still don't really get your two methods called Start
You can simply call a StartCoroutine at the end of another Coroutine so you can chain them together (though there are surely better ways to do what you want in general):
using System.Collections;
using UnityEngine;
public class CountDown : MonoBehaviour
{
public GameObject CountDownObject;
public GameObject flowerObject;
private void Start()
{
StartCoroutine(Delay());
}
private IEnumerator Delay()
{
yield return new WaitForSeconds(3);
HideCountdown();
StartCoroutine(FlowerDelay());
}
private void HideCountdown()
{
CountDownObject.SetActive(false);
}
private IEnumerator FlowerDelay()
{
yield return new WaitForSeconds(8);
ShowFlower();
}
private void ShowFlower()
{
flowerObject.SetActive(true);
}
}
I personaly don't like Coroutines .. they are not so easy to debug sometimes. I would prefer doing something like this with simple timers (though in the first moment it does look worse). Advantage is I can now directly watch the timer count down in the inspector:
using UnityEngine;
public class SimpleCountDown : MonoBehaviour
{
[Header("The Objects")]
public GameObject CountDownObject;
public GameObject FlowerObject;
[Header("Settings")]
// Here you can adjust the delay times
public float StartOffset = 3;
public float FlowerOffset = 8;
[Header("Debug")]
public float startTimer;
public float flowerTimer;
public bool isStartDelay;
public bool isFlowerDelay;
private void Start()
{
startTimer = StartOffset;
flowerTimer = FlowerOffset;
isStartDelay = true;
}
private void Update()
{
if (!isStartDelay && !isFlowerDelay) return;
if (isStartDelay)
{
startTimer -= Time.deltaTime;
if (startTimer <= 0)
{
HideCountdown();
isStartDelay = false;
isFlowerDelay = true;
}
}
if (isFlowerDelay)
{
flowerTimer -= Time.deltaTime;
if (flowerTimer <= 0)
{
ShowFlower();
isFlowerDelay = false;
this.enabled = false;
}
}
}
private void HideCountdown()
{
CountDownObject.SetActive(false);
}
private void ShowFlower()
{
FlowerObject.SetActive(true);
}
}
Related
I made a player in my game, such that it goes into slow-motion when you hold down the space bar. But I want the player to only be available to be in slow-motion for 5 seconds at a time. After 10 seconds the player will be available to go into slow-motion again.
Here is the code for the script
using UnityEngine;
public class SlowMotion : MonoBehaviour
{
public float slowMotionTimescale;
private float startTimescale;
private float startFixedDeltaTime;
void Start()
{
startTimescale = Time.timeScale;
startFixedDeltaTime = Time.fixedDeltaTime;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartSlowMotion();
}
if (Input.GetKeyUp(KeyCode.Space))
{
StopSlowMotion();
}
}
private void StartSlowMotion()
{
Time.timeScale = slowMotionTimescale;
Time.fixedDeltaTime = startFixedDeltaTime * slowMotionTimescale;
}
private void StopSlowMotion()
{
Time.timeScale = startTimescale;
Time.fixedDeltaTime = startFixedDeltaTime;
}
}
You can use IEnumerator to run time-dependent methods. The method description is as follows:
public bool inTimer; // are slow motion is in timer?
public IEnumerator StartTimer()
{
inTimer = true;
StartSlowMotion();
yield return new WaitForSeconds(5f); // wait to end slow motion
StopSlowMotion();
yield return new WaitForSeconds(5f); // wait time to finish
inTimer = false;
}
In addition, you need to consider the condition not in timer.
if (Input.GetKeyDown(KeyCode.Space) && !inTimer)
{
StartCoroutine(StartTimer()); // how to run timer
}
What I'm trying to do is have my player stop moving so I can play animation through a trigger-event or just a custscene.
What I expect was for one of them to trigger the timer then play animation>player move.. like a cut-scene
Also there are no errors In any of them They just didn't stop player from moving or just stop the player when starting the game(or when I press play it just set forwardMovement to 0)
Here's the code I use:
public class Player_controls : MonoBehaviour
{
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class Player_controls : MonoBehaviour
{
public int forwardMovement = 1000;
public void Update()
{
rb.AddForce(0, 0, forwardMovement * Speed * Time.deltaTime);
}
}
}
Things that I tried:
public void ForwardMovement()
{
forwardMovement = 0;
Invoke("Action", 2f);
}
public void Action ()
{
forwardMovement = 1000 ;
}
Which only works for ForwardMovement(); method nothing else
I tried these two things also:
Public static class MonoExtensions
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public float Delayer = 10f;
public static class MonoExtensions
{
public static void Timed_delay(this MonoBehaviour mono,Action function, float Delayer)
{
mono.StartCoroutine(Timed_delayRoutine(function,Delayer));
}
static IEnumerator Timed_delayRoutine(Action function, float Delayer)
{
yield return new WaitForSeconds(Delayer);
function();
}
public class `Player_Controls`: MonoBehaviour
{
public void PM ()
{
forwardMovement = 0;
}
void OnTriggerEnter(Collider other)
{
if (CompareTag("Power_up"))
{
this.Timed_delay(PM, Delayer);
}
}
}
Which just stop the player from moving an nothing else.
What I also tried was:
public class Player_Controls : MonoBehaviour
{
void Start()
{
StartCoroutine(ForwardMovement());
}
IEnumerator ForwardMovement()
{
forwardMovement = 0;
yield return new waitforseconds(Delayer);
forwardMovement = 1050;
}
void OnTriggerEnter(collider other)
{
if (CompareTag("Power_up"))
{
ForwardMovement();
}
}
}
Which did not work as in(it works but the timer part does nothing, so the player can't move when started the game )
Different way I tried
public class Player_Controls : MonoBehaviour
{
OnTriggerEnter(collider other)
{
Startcroutine(ForwardMovement ));
}
}
it didn't work but no Errors.
I tried this one by jazzhar:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class Player_controls : MonoBehaviour
{
// Drag the player in the inspector here;
public Rigidbody rb;
// Youre speed variable for movement.
public float Speed = 5;
// Youre speed variable for movement.
public int forwardMovement = 1000;
// Set this "stopmoving" bool to true to prevent movement.
public bool stopMoving = false;
// Create and select the right layer in the inspector.
public LayerMask WichObjectStopsMovement;
public void Update()
{
// if stopMoving = true than Dont Add force.
if (stopMoving == false)
{
rb.AddForce(0, 0, forwardMovement * Speed * Time.deltaTime);
}
}
private void OnCollisionEnter(Collision collision)
{
// if you're player touches a object and that object's layer
// is the same as "WichObjectStopsMovement" than disable Movement.
if (collision.collider.gameObject.layer == WichObjectStopsMovement)
{
stopMoving = false;
}
}
}
It sorta works in a way if you set if
if (stopMoving == true)
{
rb.AddForce(0, 0, forwardMovement * Speed * Time.deltaTime);
}
and
if (collision.collider.gameObject.layer == WichObjectStopsMovement)
{
stopMoving = true;
}
then the player would be able to move but it will not trigger if hit the cube
it didn't work either but no Errors.
And some other timers I did but forgotten....
_________________________________________________________________March/6/21
public float Delayer = 10.0f;
I think found solution by doing this
private void OnTriggerEnter(Collider other)
{
if (CompareTag("Power_up"))
{
Delayer -= Time.deltaTime; <- this part giving me the error
if (Delayer => 0)
{
forwardMovement = 0;
}
}
}
but their one problem it giving me the this error Cannot convert lambda expression to type 'bool' because it is not a delegate type [Assembly-CSharp]csharp(CS1660) I don't know much about this even searching about doesn't help either
__________________________________________________________________March 28/21
this script is under player controls
this one works I made sure by doing Debug.Log("test");
void OnTriggerExit(Collider collision)
{
if (collision.gameObject.CompareTag("Power_up"))
{
forwardMovement = 0;
}
if (forwardMovement <= 0)
{
Debug.Log("test");
}
This might be a solution to you're problem, Fire-Source!:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class Player_controls : MonoBehaviour
{
// Drag the player in the inspector here;
public Rigidbody rb;
// Youre speed variable for movement.
public float Speed = 5;
// Youre speed variable for movement.
public int forwardMovement = 1000;
// Set this "stopmoving" bool to true to prevent movement.
public bool stopMoving = false;
// Create and select the right layer in the inspector.
public LayerMask WichObjectStopsMovement;
public void Update()
{
// if stopMoving = true than Dont Add force.
if (stopMoving == false)
{
rb.AddForce(0, 0, forwardMovement * Speed * Time.deltaTime);
}
}
private void OnCollisionEnter(Collision collision)
{
// if youre player touches a object and that object's layer
// is the same as "WichObjectStopsMovement" than disable Movement.
if (collision.collider.gameObject.layer == WichObjectStopsMovement)
{
stopMoving = false;
}
}
}
This code uses a LayerMask, to create 1 click on any object and in the inspector go:
Click on Layer:
Add Layer:
Write in User Layer 6, StopMovement (or anything else).
Than in the object that should stop player movement:
Click on layer:
Than select the Layer that says StopMovement.
And in you're player's script drag the object into the rb variable.
and in "WichObjectStopsMovement" assign the StopMovement layer.
This should take care of everything is that ok?
I'm a beginner making my first game in Unity, following Unity's Create With Code course. I'm creating a shooter game that will use hand tracking. I haven't set up hand tracking yet so i created an OnTrigger input that explodes objects when I hit space. I created the spawn manager below to spawn waves of enemy attack, but they all the waves are spawning enemies too fast. It seems like they're spawning automatically instead of when the first wave has been destroyed.
Is there an easier way to spawn at a slower rate? Or spawn only when there are no more enemies alive?
EDIT: Added Attack script below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnAttack : MonoBehaviour
{
public GameObject Trumps;
private float spawnRange = 9;
public int enemyCount;
public int waveNumber = 1;
void Start()
{
SpawnEnemyWave(waveNumber);
//InvokeRepeating("GenerateSpawnPosition", startDelay, randomInterval);
}
// Update is called once per frame
void Update()
{
enemyCount = FindObjectsOfType<Attack>().Length;
if(enemyCount == 0)
{
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
void SpawnEnemyWave(int enemiesToSpawn)
{
for (int i = 0; i < enemiesToSpawn; i++)
{
Instantiate(Trumps, GenerateSpawnPosition(), Trumps.transform.rotation);
}
}
private Vector3 GenerateSpawnPosition()
{
float spawnPosX = Random.Range(-spawnRange, spawnRange);
float spawnPosZ = Random.Range(-spawnRange, spawnRange);
Vector3 randomPos = new Vector3(spawnPosX, 0, spawnPosZ);
return randomPos;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Attack : MonoBehaviour
{
public float speed = 0.5f;
public GameObject Player;
private Rigidbody enemyRb;
// Start is called before the first frame update
void Start()
{
enemyRb = GetComponent<Rigidbody>();
Player = GameObject.Find("Player");
this.transform.LookAt(Player.transform);
}
// Update is called once per frame
void Update()
{
Vector3 lookDirection = (Player.transform.position - transform.position).normalized;
enemyRb.AddForce(lookDirection * speed);
transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
private void OnTriggerEnter(Collider other)
{
Destroy(gameObject);
Debug.Log("Game Over");
}
}
I guess that you can use the invoke method:
Invoke("NameOfTheMethod", 1f)
What this method does is that it waits a certain amount of seconds before calling a method. You have to write the name of the method in quotation marks and then select how long you want to wait before the method is called (The "1f" represents the delay in seconds.) In your case, you can make the script wait before spawning enemies.
I don't know your Attack script but I would use something like
public class Attack : MonoBehaviour
{
public static readonly HashSet<Attack> AliveAttackInstances = new HashSet<Attack>();
private void Awake()
{
AliveAttackInstances.Add(this);
}
private void OnDestroy()
{
AliveAttackInstances.Remove(this);
}
}
So you can all the time in a cheaper way check how many and which enemies are alive like
public class SpawnAttack : MonoBehaviour
{
// I would change this type here to make sure your spawned prefab actually
// HAS an Attack attached .. otherwise your enemyCount will always be 0
public Attack Trumps;
...
void Update()
{
if(Attack.AliveAttackInstances.Count == 0)
{
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
Then in order to add a certain delay before spawning the next wave you could use a simple timer like
public class SpawnAttack : MonoBehaviour
{
public Attack Trumps;
[SerializeField] private float delay = 1f;
private float timer;
...
void Update()
{
if(Attack.AliveAttackInstances.Count == 0)
{
timer -= Time.deltaTime;
if(timer <= 0)
{
timer = delay;
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
}
Try to use Coroutine.
Here's a video about Coroutines: https://www.youtube.com/watch?v=qolMYyq0nX0
My example:
public class Spawn : MonoBehaviour {
private float TimeToWait = 3f;
public int enemyCount = 0;
public int waveNumber = 0;
public GameObject enemy;
void Start()
{
StartCoroutine(SpawnEnemyWave(waveNumber));
}
void Update()
{
if (enemyCount == 0)
{
waveNumber++;
StartCoroutine(SpawnEnemyWave(waveNumber));
}
if (Input.GetMouseButtonDown(0))
{
enemyCount--;
}
}
IEnumerator SpawnEnemyWave(int enemiesToSpawn)
{
//"Things to do before the seconds."
for (int i = 0; i < enemiesToSpawn; i++)
{
enemyCount++;
}
yield return new WaitForSeconds(TimeToWait); // at this example we wait 3 seconds (float TimeToWait = 3f;)
//"Things to do after the seconds."
for (int i = 0; i < enemiesToSpawn; i++)
{
Debug.Log("New Enemy!");
Instantiate(enemy, transform.position, Quaternion.identity);
}
}
}
I'm doing an educational game for kids..
But I stopped at the end of the scene
I could not make a code to start the new scene..
in the First Script
when play game the scenes does not stop until the last scene.
YouWin.pictureInPlace++;
I searched a lot and did not find my question, so I consulted you. It was easier to do a button to go to the Next scene but I prefer to do it automaticly
I think this task can be accomplished by the boolean but its need to reference game object.. and the script on 2 images.
The First Script (Manager) has put on Four images At Canvas..
The second one (YouWin) I put on empty GameObject..
thanks for the help
The first script (Manger)
using UnityEngine;
using UnityEngine.EventSystems;
public class Manager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
Vector2 pos1;
public GameObject pos2;
private bool canMove;
public GameObject winner;
void Start()
{
pos1 = transform.position;
canMove = true;
}
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if (canMove)
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
float distance = Vector3.Distance(transform.position, pos2.transform.position);
if (distance < 50)
{
transform.position = pos2.transform.position;
transform.localScale = pos2.transform.localScale;
canMove = false;
winner.GetComponent<YouWin>().pictureInPlace++;
}
else
{
transform.position = pos1;
}
}
}
The second script (YouWin)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class YouWin : MonoBehaviour
{
public int NumberOfImages;
public int pictureInPlace;
public string sceneName;
void Update()
{
if (pictureInPlace == NumberOfImages)
{
StartCoroutine(LoadScene());
Debug.Log("You Win!");
}
}
IEnumerator LoadScene()
{
yield return new WaitForSeconds(1.5f);
SceneManager.LoadScene(sceneName);
}
}
It looks like you didn't reference YouWin in your Manager script. You should include it by adding it as a global variable public YouWin <variable_name>; or by referencing your empty GameObject and getting the YouWin component:
public GameObject emptyObject;
emptyObject.GetComponent<YouWin>().picturesInPlace++;
After all thanks for helping me but I was able to find the right solution
just make in the first Script (Manager) :
bool static Done;
void Start()
{
bool Done = false;
}
public void OnEndDrag(PointerEventData eventData)
{
float distance = Vector3.Distance(transform.position, pos2.transform.position);
if (distance < 50)
{
transform.position = pos2.transform.position;
transform.localScale = pos2.transform.localScale;
canMove = false;
bool Done = True;
}
}
and just call it from the second Script (YouWin)
public string sceneName;
void Update()
{
if(Youwin.Done)
{
StartCoroutine(LoadScene());
}
}
IEnumerator LoadScene()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene(sceneName);
}
This IS The right solution ;
using UnityEngine;
using UnityEngine.Networking;
public class Interactable : NetworkBehaviour {
public float radius = 2f;
bool isFocus = false;
Transform player;
bool hasInteracted = false;
public Transform interactionPoint;
public virtual void Interact()
{
// This method is meant to be overwritten
Debug.Log("Interacting with " + transform.name);
}
private void Update()
{
if (isFocus && !hasInteracted)
{
float distance = Vector3.Distance(player.position, interactionPoint.position);
if(distance <= radius)
{
Interact();
hasInteracted = true;
}
}
}
public void onFocused(Transform Player)
{
isFocus = true;
player = Player;
}
public void onDeFocused()
{
isFocus = false;
player = null;
hasInteracted = false;
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, radius);
}
}
This is for the interaction which is overwritten.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class SphereBehaviour : Interactable {
public float countdown = 1;
public float timeLeft = 5f;
public override void Interact()
{
Debug.Log("Interacting with the Sphere object");
StartCoroutine(StartCountdown());
}
public IEnumerator StartCountdown() // Countdown from 5
{
countdown = timeLeft;
while (countdown > 0)
{
yield return new WaitForSeconds(1.0f);
countdown--;
}
}
The Problem is that this isn't networked and I can't think of the logic to do it. An Interface won't work or anything. Is there any possible solution which doesn't require changing everything?
I'm just trying to turn Brackeys set of youtube tutorials to something multiplayer.
I'm assuming you're using the public "countdown" variable for some UI element.
If you want each player to see their own countdown when they themselves touch this Sphere, this should work fine.
If anyone touching the sphere should trigger a countdown for everyone, then this will have to be done differently. You'll want the host/server to track interactions between players and spheres, then trigger the countdown coroutine in the clients.
I personally don't recommend #1 in any case, as collisions/interactions should be done server-side.