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);
}
}
Related
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
Hello I'm a newbie and I have an error I searched google and there came up results but they used to work I remember but I guess it updated anyway whenever I write this:
m_Rigidbody.velocity.magnitude = m_Max_SpeedBACK;
(Yes, I didn't forget to make the float itself)
And it shows this error
enter image description here
It said I'm not allowed to send img but anyway I want to have this if written simply
(rb.speed = float)
Can anyone help, thanks?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class my_Char_Cont : MonoBehaviour
{
public Rigidbody m_Rigidbody;
public float m_Thrust_Forth = 20f;
public float m_Thrust_Behind = -20f;
public float m_time = 10f;
public float m_timeNeg = -10f;
public float m_Max_SpeedFORTH = 4f;
public float m_Max_SpeedBACK = 2f;
// Start is called before the first frame update
void Start()
{
m_Rigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
bool forwardPress = Input.GetKey("w");
bool backPress = Input.GetKey("s");
bool leftShiftPress = Input.GetKey("left shift");
//---------------------------------------------------------------------------------------------------//
if (forwardPress)
{
m_time = m_time + Time.deltaTime;
if (m_time >= 4)
{
m_time = 3.9f;
}
m_Rigidbody.AddForce(transform.forward * m_Thrust_Forth * m_time);
}
else
{
m_time = 0;
}
if (backPress)
{
m_timeNeg = m_timeNeg + Time.deltaTime;
if (m_timeNeg >= 4)
{
m_timeNeg = 3.9f;
}
m_Rigidbody.AddForce(transform.forward * m_Thrust_Behind * m_timeNeg);
if (m_Rigidbody.velocity.magnitude >= m_Max_SpeedBACK)
{
m_Rigidbody.velocity.magnitude = m_Max_SpeedBACK;
}
}
else
{
m_timeNeg = 0;
}
}
}
The velocity can be read, but not written like that. As #Ruzihm already explained.
If you want to limit the velocity while keeping the direction, do this:
if (m_Rigidbody.velocity.magnitude >= m_Max_SpeedBACK)
{
m_Rigidbody.velocity = m_Rigidbody.velocity.normalized * m_Max_SpeedBACK;
}
normalized returns the vector, normalized so that the length (magnitude) is 1. But to clarify: it is not "(1,1,1)" - the direction stays. Example: (0.802, 0.267, 0.534) has a magnitude of 1.
Therefore, you can assign the velocity to a value, that equals the normalized velocity * your max allowed magnitude.
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());
}
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.
The first script is for the F pressing and calling the ScaleChange:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroidMove : MonoBehaviour
{
public GameObject droid;
public ChangeScale changeScale;
private bool toDisplay = false;
private void Start()
{
droid.transform.localScale = new Vector3(0, 0, 0);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
toDisplay = !toDisplay;
changeScale.Scale(toDisplay);
}
}
}
The second script make the scaling:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeScale : MonoBehaviour
{
public GameObject objectToScale;
private float _currentScale = InitScale;
private const float TargetScale = 0.7f;
private const float InitScale = 0f;
private const int FramesCount = 10;
private const float AnimationTimeSeconds = 0.1f;
private float _deltaTime = AnimationTimeSeconds / FramesCount;
private float _dx = (TargetScale - InitScale) / FramesCount;
private IEnumerator ScaleUp()
{
bool upscaling = true;
while (upscaling)
{
_currentScale += _dx;
if (_currentScale > TargetScale)
{
upscaling = false;
_currentScale = TargetScale;
}
objectToScale.transform.localScale = Vector3.one * _currentScale;
yield return new WaitForSeconds(_deltaTime);
}
}
private IEnumerator ScaleDown()
{
bool downscaling = true;
while (downscaling)
{
_currentScale -= _dx;
if (_currentScale < InitScale)
{
downscaling = false;
_currentScale = InitScale;
}
objectToScale.transform.localScale = Vector3.one * _currentScale;
yield return new WaitForSeconds(_deltaTime);
}
}
public void Scale(bool scaleUp)
{
if (scaleUp)
{
StartCoroutine(ScaleUp());
}
else
{
StartCoroutine(ScaleDown());
}
}
}
The problem is if I press on F and it start scaling up and not finished yet the scaling and I press F again instead start scaling down it's like pausing and if I keep pressing F it will keep scaling up each time a bit.
But what I want to do is when I press first time F start scaling up and if in the middle while scaling i'm pressing F again then from the current scaling point start scaling down and if I press F again then up and down like a smooth switch.
But now it's just pausing each time I press F many times.
It's like I need to wait now for the scaling to finish first before I can press F again if not it will just pause it and i want that each time I press F it will switch the scaling up/down.
You don't need multiple functions for scaling up and down. One should do it. Just make it take a scale amount as parameter then re-use that function. The scale Min and Max should be a Vector3 not float since that's how scale is represented and you want to cover all three axis (x,y,z).
How to fix the problem of scaling up taking so long to finish and not switching direction when key is pressed:
When you start a coroutine, get a reference to that running coroutine. Before you start the coroutine again, use the old reference to stop the old one. It's as simple as that. It's much better to re-write the whole code than to fix the existing one you have.
Below is a simplified version of your code. Make sure to set the minSize, maxSize and objectToScale variables from the Editor. See code comments if you have any question.
public GameObject objectToScale;
public Vector3 minSize;
public Vector3 maxSize;
private bool scaleUp = false;
private Coroutine scaleCoroutine;
// Use this for initialization
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
//Flip the scale direction when F key is pressed
scaleUp = !scaleUp;
//Stop old coroutine
if (scaleCoroutine != null)
StopCoroutine(scaleCoroutine);
//Scale up
if (scaleUp)
{
//Start new coroutine and scale up within 5 seconds and return the coroutine reference
scaleCoroutine = StartCoroutine(scaleOverTime(objectToScale, maxSize, 5f));
}
//Scale Down
else
{
//Start new coroutine and scale down within 5 seconds and return the coroutine reference
scaleCoroutine = StartCoroutine(scaleOverTime(objectToScale, minSize, 5f));
}
}
}
IEnumerator scaleOverTime(GameObject targetObj, Vector3 toScale, float duration)
{
float counter = 0;
//Get the current scale of the object to be scaled
Vector3 startScaleSize = targetObj.transform.localScale;
while (counter < duration)
{
counter += Time.deltaTime;
targetObj.transform.localScale = Vector3.Lerp(startScaleSize, toScale, counter / duration);
yield return null;
}
}