I have a code that once every 24 hours (86400000 milliseconds) if the player's score is over 7000 points, then it divides all points above 7000 by 2, but the problem is that when I get 7000+ points, the game freezes and no longer works never (everything worked well in unity). What could be the reason?
using System;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using Random=UnityEngine.Random;
public class LeagueClock : MonoBehaviour
{
private ulong TimeLastReset;
[SerializeField] private float msToWait = 86400000f;
[SerializeField] private int scoreThreshold = 7000;
private int scrMain;
private int MainCoins;
private int LegendaryTrophies;
private void Start(){
LegendaryTrophies = PlayerPrefs.GetInt("LegendaryTrophies");
scrMain = PlayerPrefs.GetInt("scrMain");
TimeLastReset = ulong.Parse(PlayerPrefs.GetString("LastReset", "0"));
MainCoins = PlayerPrefs.GetInt("MainCoins");
Debug.Log(DateTime.Now);
}
private void Update(){
if (scrMain > 7000) {
//apply the action for each interval that has passed.
//For example, if the interval is 24 hours, and 49 hours
//have passed, that's 2 intervals, so we reduce the score
//twice.
int intervalsPassed = GetIntervalsPassed();
if (intervalsPassed > 0){
for (int i = 0; i < intervalsPassed; i++) {
ReduceScore();
}
PlayerPrefs.SetInt("scrMain", scrMain);
TimeLastReset = (ulong)DateTime.Now.Ticks;
PlayerPrefs.SetString("LastReset", TimeLastReset.ToString());
}
}
}
private void ReduceScore() {
MainCoins += ((scrMain-scoreThreshold)/2)*100;
LegendaryTrophies += (scrMain-scoreThreshold)/2;
scrMain -= (scrMain-scoreThreshold)/2;
PlayerPrefs.SetInt("MainCoins", MainCoins);
PlayerPrefs.SetInt("scrMain", scrMain);
PlayerPrefs.SetInt("LegendaryTrophies", LegendaryTrophies);
PlayerPrefs.Save();
}
//returns the number of full time intervals that have passed since
//we last reduced the score
private int GetIntervalsPassed(){
ulong diff = ((ulong)DateTime.Now.Ticks - TimeLastReset);
ulong ms = diff / TimeSpan.TicksPerMillisecond;
float secondsLeft = (float)(msToWait - ms) / 1000.0f;
int intervalsPassed = Mathf.FloorToInt(ms / msToWait);
Debug.Log($"SecondsLeft: {secondsLeft} | Intervals: {intervalsPassed} | Score: {scrMain}");
return intervalsPassed;
}
}
It might memory problem that affects your game you can look at these documents, and I hope this helps Unity Memory
Related
here is my code and i have no idea how to work it up. im a beginner in c# and this is for our project.
the project is about a balloon popper and i am having trouble to set an amount of balloons to spawn.
I am planning to set the amount of balloons to spawn at 5, 10 even 20 and after that, the spawning will stop.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnScript: MonoBehaviour
{
public Transform[] spawnPoints;
public GameObject[] balloons;
public float spawnTime = 0f;
float spawnTimeLeft = 0f;
// Start is called before the first frame update
void Update()
{
if (spawnTimeLeft >= spawnTime)
{
int randBalloon = Random.Range(0, balloons.Length);
int randSpawnPoint = Random.Range(0, spawnPoints.Length);
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
spawnTimeLeft = 0f;
}
else
{
spawnTimeLeft = spawnTimeLeft + Time.deltaTime;
}
}
}
Add a variable to the class for the maximum amount. Have done this as public so you can set it in the inspector to the amount you wish.
Also add a counter variable and set it to 0. This is private as nothing but this class needs access to it. Do serialize this field.
public int spawnAmountMax = 5;
[SerializeField]
private int spawned = 0;
Start the Update() with the next check, return from the method when it evaluates to true.
if (spawned >= spawnAmountMax)
return;
Than after instantiating the GameObject, increase the variable:
spawned++;
The adjusted code
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
public class SpawnScript : MonoBehaviour
{
public int spawnAmountMax = 5;
private int spawned = 0;
public Transform[] spawnPoints;
public GameObject[] balloons;
public float spawnTime = 0f;
float spawnTimeLeft = 0f;
// Start is called before the first frame update
void Update()
{
if (spawned >= spawnAmountMax)
return;
if (spawnTimeLeft >= spawnTime)
{
int randBalloon = Random.Range(0, balloons.Length);
int randSpawnPoint = Random.Range(0, spawnPoints.Length);
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
spawnTimeLeft = 0f;
spawned++;
} else
{
spawnTimeLeft = spawnTimeLeft + Time.deltaTime;
}
}
}
Try this code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnScript: MonoBehaviour
{
public Transform[] spawnPoints;
public GameObject[] balloons;
public float spawnTime = 5.0f;
public float maxSpawnTime = 20.0f;
private void Start()
{
Invoke(nameof(Spawn), spawnTime); //This will start the spawning process after the initially set spawnTime, so first this will wait for 5 seconds.
}
private void Spawn()
{
int randBalloon = Random.Range(0, balloons.Length);
int randSpawnPoint = Random.Range(0, spawnPoints.Length);
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
spawnTime *= 2; //doubles the spawnTime, according to your request, so the second time this will be 10, and the third time, this will be 20 seconds
if (spawnTime <= maxSpawnTime) //check if we reached or not the maximum spawn time
{
Invoke(nameof(Spawn), spawnTime); //if we not yet reached the maximum spawn time, it will start to wait and spawn one more balloon again.
}
}
}
since you're doing this in you update, you can add a counter after your Instantiate call.
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
counter++;
And then, nest your if statement inside another if checking for the counter.
if(counter < amountToSpawn)
{
//your code here
}
For me personally, I would make a Coroutine method with delay that will have a for loop inside, rather than doing this in update.
Sample:
private IEnumerator SpawnObjects(int spawnCount)
{
for(int i = 0; i < spawnCount; i++)
{
//Instantiate here
yield return new WaitForSeconds(5);
}
}
To call the method:
StartCoroutine(SpawnObjects(10));
I am working through the unity tutorials and I was wondering how to stop a timer when you hit 12 of count.
I currently have two scripts I am using.
PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;
public class PlayerController : MonoBehaviour
{
public float speed;
public TextMeshProUGUI countText;
public GameObject winTextObject;
private float movementX;
private float movementY;
private Rigidbody rb;
private int count;
// At the start of the game..
void Start()
{
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText();
winTextObject.SetActive(false);
}
void FixedUpdate()
{
// Create a Vector3 variable, and assign X and Z to feature the horizontal and vertical float variables above
Vector3 movement = new Vector3(movementX, 0.0f, movementY);
rb.AddForce(movement * speed);
}
void OnTriggerEnter(Collider other)
{
// ..and if the GameObject you intersect has the tag 'Pick Up' assigned to it..
if (other.gameObject.CompareTag("Pickup"))
{
other.gameObject.SetActive(false);
// Add one to the score variable 'count'
count = count + 1;
// Run the 'SetCountText()' function (see below)
SetCountText();
}
if (other.gameObject.CompareTag("SpeedPickup"))
{
other.gameObject.SetActive(false);
speed = speed + 25;
count = count + 1;
}
}
void OnMove(InputValue value)
{
Vector2 v = value.Get<Vector2>();
movementX = v.x;
movementY = v.y;
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count >= 12)
{
winTextObject.SetActive(true);
}
}
}
And a second script Timer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class Timer : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI timerUILabel; //drag UI Text object here via Inspector
private float t_offset = 0f; // set to nothing, can be set to an offset if needed.
private int t_minutes;
private int t_seconds;
private int t_milliseconds;
private bool finished = false;
private void Update()
{
float t = Time.time - t_offset;
//Debug.Log("currentTime in seconds = " + t);
t_minutes = ((int)t / 60); // t(seconds) / 60 = total minutes
t_seconds = ((int)t % 60); // t(seconds) % 60 = remaining seconds
t_milliseconds = ((int)(t * 100)) % 100; // (total seconds * 1000) % 1000 = remaining milliseconds
//display the text in a 00:00:00 format
timerUILabel.text = string.Format("{0:00}:{1:00}:{2:00}", t_minutes, t_seconds, t_milliseconds);
}
}
My questions are
Do I need to import the Timer.cs script into the PlayerController to get this functionality to work? If so how? (is it as simple as using a using statement?)
I am thinking I need to put in an additional conditional to change the win condition from false to true. Is that the right path?
Thanks for all your help!
Why not use a coroutine?
private int _time;
private Coroutine _cr;
void Start ()
{
//Call this to start it and save coroutine to a variable
_cr = StartCoroutine(Timer());
//Then to stop it call:
//StopCoroutine(_cr);
//And read the value of _time to get seconds since start.
}
private IEnumerator Timer ()
{
yield return new WaitForSeconds(1);
_time++;
_cr = StartCoroutine(Timer());
}
The minimum is 20 the maximum is 300.
If in the inspector for example the current value of the slider is 120 then pick a random number between 20 and 120 and if before running the game or while the game is running I'm changing the value to 77 then the next random number should be between 20 and 77.
I want that each X random seconds each time that the npcs will do something.
For example after running the game if the random number is 20 or 90 then after 20 seconds or 90 seconds the npcs will do something. Then the next random number if it's 199 then after 199 seconds the npcs will do the same thing(something) and so on.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class RandomTester : MonoBehaviour
{
public List<GameObject> npcs;
[Range(20, 300)]
public int timeVisit = 20;
public string nextVisitTime;
// Start is called before the first frame update
void Start()
{
npcs = GameObject.FindGameObjectsWithTag("Npc").ToList();
}
// Update is called once per frame
void Update()
{
}
}
To generate X radom numbers between 20 and 300 and add them to a list:
Random r = new Random();
List<int> rands = new List<int>();
for (int i = 0; i < X; i++)
{
rands.Add(r.Next(20, 300));
}
Maybe I didn't explain it good.
This is what I wanted and is working. I'm taking the random number from between the minimum and current values of the slider of the range attribute the slider in the inspector.
Then later I will make that each time I'm changing the value in the inspector and it's picking up a random number it will start a coroutine that will wait the random picked seconds when it finish the npcs will do something.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class RandomTester : MonoBehaviour
{
public List<GameObject> npcs;
[Range(20, 300)]
public int timeVisit = 20;
public string nextVisitTime;
private int oldtimevisit;
// Start is called before the first frame update
void Start()
{
npcs = GameObject.FindGameObjectsWithTag("Npc").ToList();
oldtimevisit = timeVisit;
}
// Update is called once per frame
void Update()
{
if(timeVisit != oldtimevisit)
{
StopAllCoroutines();
Debug.Log(Random.Range(20,timeVisit));
var timetowait = Random.Range(20, timeVisit);
StartCoroutine(Waiting(timetowait));
}
oldtimevisit = timeVisit;
}
IEnumerator Waiting(int waitingtime)
{
yield return new WaitForSeconds(waitingtime);
DoSomethingWithNpcs();
}
private void DoSomethingWithNpcs()
{
}
}
My game implements a datetime system which contains 3 speeds:
Slow: 1 second = 10 in-game minutes,
Normal: 1 second = 2 in-game hours,
Fast: 1 second = 1 in-game day
Slow speed works fine, normal speed have a bit of delay, and fast speed is delay by a lot (like 2.5 sec for a day). Is it because the computer cannot handle too much operation? Is there any way to implement it? Thanks.
Below is my script, important part starts at the update function.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
//Manage datetime
public class DateManager : MonoBehaviour
{
//Singleton
public static DateManager dateManager;
//Get other objects
private GameStatManager gameStatManager;
//Local var
private Text displayTime;
private Text displayDate;
private Image timeScaleImage;
float counter;
//Get speed icon
public Sprite slow;
public Sprite medium;
public Sprite fast;
//Output var
public DateTime date;
private float timeScale;
void Start(){
//Singleton
dateManager = this;
//Get other objects
gameStatManager = GameObject.Find("GameStatManager").GetComponent<GameStatManager>();
displayDate = transform.Find("Date").gameObject.GetComponent<Text>();
displayTime = transform.Find("Time").gameObject.GetComponent<Text>();
timeScaleImage = transform.Find("TimeScale").gameObject.GetComponent<Image>();
//Set default
timeScale = 1f;
timeScaleImage.sprite = slow;
counter = 0f;
date = new DateTime(2010, 01, 01);
}
void Update()
{
counter += Time.deltaTime;
if (counter >= 1f / timeScale){
counter = 0f;
date = date.AddMinutes(10);
displayDate.text = date.ToString("dd MMM, yyyy");
displayTime.text = date.ToString("HH:mmtt");
//To GSM
gameStatManager.SetDate(date);
}
}
//Button function
public void ChangeTimeScale(){
// 1f = 10mins/sec || 12f = 2hrs/sec || 144f = 1day/sec
if (timeScale == 1f){
timeScale = 12f;
timeScaleImage.sprite = medium;
}
else if (timeScale == 12f){
timeScale = 144f;
timeScaleImage.sprite = fast;
}
else {
timeScale = 1f;
timeScaleImage.sprite = slow;
}
//To GSM
gameStatManager.SetTimeScale(timeScale);
}
}
I have a SceneController that's supposed to initialize a set of empty GameObject spawners, each working together at the same rhythm. The RockSpawners receive an array of time delays and wait the X seconds between spawning another rock.
I set the _nextSpawn = float.maxValue when the spawners start and plan to overwrite this after "Initializing" them (my own method), however even though my debug logs say I've overwritten my _nextSpawn value while initializing, the update loop is still reporting float.maxValue and nothing ends up spawning because _timeSinceLastSpawn hasn't exceeded float.maxValue seconds.
Is there something I'm missing with the scope of my _nextSpawn variables? It doesn't seem to be a "this" vs "local" issue, at least at first glance.
Debug output: 0 0 3 3. 0's stay the same, 3's will vary based on rng.
SceneController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SceneController : MonoBehaviour {
[SerializeField] private GameObject _rockSpawnerPrefab;
public int numRocks = 6;
public int minSpawnDelaySec = 1;
public int maxSpawnDelaySec = 3;
private bool spawnersInitialized = false;
void Start () {
InitializeSpawners();
}
void Update () {
}
void InitializeSpawners() {
float[] pattern = new float[numRocks];
for (int i = 0; i < numRocks; i++) {
// Generate delays at half second increments within bounds
float delay = Mathf.Floor(Random.value * ((float)(maxSpawnDelaySec + 0.5f - minSpawnDelaySec) / 0.5f));
delay = delay * 0.5f + minSpawnDelaySec;
pattern[i] = delay;
}
GameObject spawner = Instantiate(_rockSpawnerPrefab) as GameObject;
spawner.transform.position = new Vector3(0, 4, 0);
RockSpawner rockSpawnerScript = spawner.GetComponent<RockSpawner>();
rockSpawnerScript.Initialize(pattern);
}
}
RockSpawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RockSpawner : MonoBehaviour {
[SerializeField] private GameObject _rockPrefab;
public float minSpawnDelay = 3f;
public float maxSpawnDelay = 6f;
private float[] _pattern;
private int _currentPattern;
private float _timeSinceLastSpawn;
private float _nextSpawn;
void Start () {
_currentPattern = -1;
_nextSpawn = float.MaxValue;
}
void Update () {
if (_pattern == null) return;
_timeSinceLastSpawn += Time.deltaTime;
if (_timeSinceLastSpawn > _nextSpawn) {
GameObject rock = Instantiate(_rockPrefab) as GameObject;
rock.transform.position = transform.position;
NextTimer();
}
}
public void Initialize(float[] pattern) {
_pattern = pattern;
NextTimer();
}
private void NextTimer() {
_timeSinceLastSpawn = 0;
_currentPattern += 1;
Debug.Log(_nextSpawn);
Debug.Log(this._nextSpawn);
this._nextSpawn = _pattern[_currentPattern];
Debug.Log(_nextSpawn);
Debug.Log(this._nextSpawn);
}
}
It's not about scoping, it's about call order. When you create a GameObject its Start method is called on the frame it's enabled, not when the object is created. So your code will call Initialize first, then Start which overwrites the values.
Remove the code in Start and handle everything in Initialize and it should work as you want.