Jump to content
Unity Insider Forum

Timer


peachplayer

Recommended Posts

Hallo Leute

Weiss jemand wie man einen Timer mit Unity machen kann?

Habs wie folgt probiert:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.UI;

public class Countdown11 : MonoBehaviour
{
    public float timeStart = 0;
    public Text textBox;
    public DateTime time;
    // Use this for initialization
    void Start()
    {
        textBox.text = timeStart.ToString();
    }

    // Update is called once per frame
    void Update()
    {
        Time.timeScale = 500f;
        timeStart += Time.deltaTime * Time.timeScale;
        DateTimeFormatInfo fmt = (new CultureInfo("hr-HR")).DateTimeFormat;
        var time = new DateTime(Convert.ToInt64(timeStart));
        textBox.text = time.ToLongDateString();

        Debug.Log(time.ToString("yyyy-MM-ddTHH:mm:ssZ"));

    }
}

Leider bleibt die Ausgabe immer bei null stecken, statt dass die Tage vorwärts laufen.

Kann mir jemand helfen?

Gruss, peachplayer

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moin,

also erstmal solltest du timeScale nicht dafür benutzen. Das ist ein globaler Faktor, der auf Time.deltaTime draufmultipliziert wird, und zwar überall. Die Physik läuft dann auch auf 500-facher Geschwindigkeit. Und du multiplizierst das ja nochmal drauf, also bist du bei 250.000 Steigerung pro Sekunde.

Dann benutzt du da new DateTime(long), was als Parameter einen Wert in Zehntel-Millisekunden haben will. Du schmeißt also nicht 250.000, sondern 25 Sekunden pro Sekunde da rein... ganz komischer Umweg.

Aber mal zum Anfang... was meinst du mit "Timer"? Meinst du einfach eine Uhr im Spiel, die bei 0 losgeht, wenn das Spiel startet? Oder meinst du so eine Uhr, die weiterläuft, wenn das Spiel geschlossen und wieder geöffnet wurde (bzw. zumindest so tut)? Es sieht irgendwie so aus, als würdest du ein paar Ingame-Jahre verstreichen lassen wollen, wenn irgendwie eine Stunde oder so vergeht... aber ich will eigentlich nicht blind raten.

Ein Timer ist erstmal ganz einfach. Nicht timeScale benutzen, sondern einfach einen eigenen Faktor:

private float time;
[Tooltip("How many ingame seconds pass per real-world second?")]
public float factor = 1;

private void Update()
{
  time += Time.deltaTime * factor;
  
  Debug.Log(time);
}

Wenn du deinen Sekunden-Wert in Jahre, Monate, Tage, Stunden, Minuten und Sekunden rechnen willst, dann kannst du dafür theoretisch DateTime benutzen, aber damit ist immer ein reales Datum im realen Kalender repräsentiert. Passender wäre vermutlich die TimeSpan-Klasse, und dann so:

var timeSpan = new TimeSpan((long)(time * 10000));

10000, um von den 100-Nanosekunden-Schritten auf Sekunden-Schritte zu kommen. Das solltest du dann mit ToString in eine hübsch lesbare Form kriegen können, wie du es auch schon tust.

Link zu diesem Kommentar
Auf anderen Seiten teilen

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.UI;

public class Countdown11 : MonoBehaviour
{
    float cntdnw = 1.0f;
    public Text disvar;
    public Button startButton;
    public Button stoppButton;

    private void Start()
    {
        Button btn1 = startButton.GetComponent<Button>();
        Button btn2 = stoppButton.GetComponent<Button>();

    }
    void Update()

    {

        if ( (startButton.enabled) && (!stoppButton.enabled))
        {
          
            cntdnw += Time.deltaTime;



            double b = System.Math.Round(cntdnw, 2);
            string dateTimeString = System.String.Format("{0:yyyy-MM-dd-hh}", b);

            disvar.text = TimeSpan.FromDays(b).ToString();
        }
       
  if (stoppButton.enabled)
                Debug.Log("game stopped!");
    }
}

Habe noch 2 Buttons für "start" und "stopp" gemacht. Jedoch funktionieren diese Buttons nicht richtig, d.h. das Spiel kann nicht fortgesetzt werden nach einem stopp.

Gruss, peachplayer

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du fragst ab, ob die Button-Komponente Enabled ist. Das hat ja nichts mit einem Druck auf den Button zu tun. Enabled wird die Komponente immer sein.

Es scheint, als hättest du noch keine Erfahrung mit der UI, also den Buttons da drin.

Der Button ist ein Gameobjekt und hat eine Button-Komponente ( Das unterscheidet ihn von einem Image, das hat nämlich stattdessen eine Image-Komponente).
Die Button-Komponente hat ein OnClick Event, welches dafür da ist, in eine  ( oder auch mehrere) Methode(n) eines Scripts hinein zu springen, wenn der Button gedrück wurde.
Du brauchst also ein eine public Methode in deinem Script, mit der der Button kommuniziert.

z.B. sowas:
 

public void StartCounter(){
  // diese Methode soll der Start Button aufrufen
  // hier kannst du eine Variable setzen oder aber ganzen Code ausführen
}
public void StopCounter(){
  // Diese Methode soll der Stop Button aufrufen
  // auch hier kannst du alles Möglich drin tun
}

Du klickst also beim Bereich OnClick() auf das kleine Pluszeichen. Jetzt legst du in den entstandenen Slot das Objekt rein, auf dem dein Script liegt.

Nun kannst du beim Dropdown  Function dein Script sehen. Gehe mit der Maus über das Script und es öffnet sich ein weiterer Bereich, der dir alle möglichen Dinge anzeigt. Unter anderem auch deine public Methoden. Wähle die gewünschte Methode aus. Schon ist der Button mit dem Script verknüpft.

Noch was:
Du hast ja in deinem Script die 2 Buttons eingebunden. Ich vermute du setzt per script  enable auf true oder false, weil du möchtest, dass die Button nur dann etwas machen sollen wenn das Szenario es auch erlaubt.
Buttons enabled man aber nicht. Man sagt ihnen ob sie interagieren dürfen oder nicht. Und dafür gibt es extra einen Zustand der interactable heisst.
Und das steuerst du so:

StopButton.interactable = true; 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke für die Tipps!

Wenn ich das richtig verstanden habe, benötige ich keinen Event-Handler für die Buttons, um auf einen Click zu regarieren?

Ein weiteres Problem  ist die Anweisung:

cntdnw += Time.deltaTime;

Muss dieser Befehl nicht zwingend innerhalb der Update()-Funktion aufgeführt werden, damit der Timer überhaupt vorwärts läuft?

Gruss, peachplayer

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 33 Minuten schrieb peachplayer:

Wenn ich das richtig verstanden habe, benötige ich keinen Event-Handler für die Buttons, um auf einen Click zu regarieren?

Korrekt. Du kannst entweder die Methode(n) aussuchen, die der Button beim Klick ausführen soll, indem du im Inspektor des Buttons Listeneinträge machst, oder du machst das über Code, mit mybutton.onClick.AddListener. Beides ist valide. Welches von beidem du eher nutzen solltest, hängt von der Situation ab.

vor 35 Minuten schrieb peachplayer:

Ein weiteres Problem  ist die Anweisung:

cntdnw += Time.deltaTime;

Muss dieser Befehl nicht zwingend innerhalb der Update()-Funktion aufgeführt werden, damit der Timer überhaupt vorwärts läuft?

Richtig, du willst das immer wieder ausführen. Aber du willst nicht immer wieder den Button abfragen. Geht auch gar nicht.

Was du tun willst, ist einen Schalter umlegen:

private bool timerIsActive;

public void StartTimer()
{
  timerIsActive = true;
}

public void PauseTimer()
{
  timerIsActive = false;
}

private void Update()
{
  if (timerIsActive)
  {
    // Do clock stuff
  }
}

Du kannst aber auch direkt enabled benutzen:

public void StartTimer()
{
  enabled = true;
}

public void PauseTimer()
{
  enabled = false;
}

private void Update()
{
  // Do clock stuff
}

Update wird ja gar nicht erst aufgerufen, wenn enabled false ist. Außerdem kannst du dann schön im Editor manuell das Häckchen an und aus machen zum Debuggen :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Leider funktioniert immer noch nicht alles ganz richtig:

public void starttimer()
    {
        startButton.enabled = true;
        stoppButton.enabled = false;
        stoppButton.interactable = true;
    }

    public void stopptimer()
    {
        stoppButton.enabled = true;
        startButton.enabled = false;
        startButton.interactable = true;
    }

Der Timer kann nach einem 'stop' nicht mehr fortgesetzt werden mit dem 'start' - Button. Weshalb wohl?

Gruss, peachplayer

Link zu diesem Kommentar
Auf anderen Seiten teilen

Was soll denn dein enable.true bzw. enabled.false bewirken? Wieso machst du das?
Wenn du eine Komponente über enabled.false abschaltest, dann geht sie auch nicht mehr bis du sie wieder einschaltest.

Drückst du den Stop Button, wird der Startbutton disabled. Er ist also deaktiviert und funktioniert nicht mehr. Deswegen kannst du es nicht fortsetzen.
Deswegen frage ich dich, warum du enable nutzt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

NEIN! Also... doch. Aber bitte nicht. PlayerPrefs speichert Dinge auf der Festplatte. Das ist NICHT dafür da, dass du Dinge in mehreren Szenen zur Verfügung hast. Ist ein bisschen als ob du das Waschbecken abmontierst, um damit in die Küche zu gehen und Suppe reinzufüllen. Geht zwar, aber lass mal lieber.

Wenn du Dinge szenenübergreifend machen willst, musst du dir bewusst machen, was ein Szenenwechsel ist. Es werden alle GameObjects gelöscht und die GameObjects der neuen Szene werden geladen. Es geht alles verloren, was auf einem der gelöschten GameObjects hockt... naja, theoretisch zumindest ;)

Davon ausgehend gibt es mehrere Sachen, die du machen kannst. An dieser Stelle denke ich, dass ein statisches Feld vermutlich sinnvoll wäre. Ein statisches Feld ist ja eines, das nicht an ein Objekt gebunden ist. Der Wert geht also auch nicht flöten, wenn alle möglichen Objekte gelöscht werden, weil er halt zu keinem davon gehört.

Du machst also statt

private float zahl;

einfach

private static float zahl;

und schon bleibt der Wert erhalten. Denke aber daran, was static bedeutet und was das für Konsequenzen hat. Du kannst nämlich zum Beispiel keine zwei Timer dieser Art mehr haben, weil ja nicht jedes Timer-Objekt sein eigenes float-Feld bekommt, sondern es programmweit nur noch dieses eine gibt.

Wenn static hier nicht die richtige wahl sein sollte (weil du z.B. doch beliebig viele Timer haben können willst), dann könnten noch DontDestroyOnLoad oder ScriptableObjects etwas für dich sein.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor einer Stunde schrieb peachplayer:

Muss denn die statische Varible im anderen Skript nicht via Update inkrementiert werden, damit das Datum nicht bei Null stehen bleibt?

Doch. :)

vor einer Stunde schrieb peachplayer:

In der Anfangs-Szene wo der Timer generiert wird

Ich glaube, da ist vielleicht der Knackpunkt. Ein statisches Feld existiert nicht auf einem Objekt. Es wird also auch nicht generiert. Es existiert von Anfang des Programms bis zu dessen Ende. Ein stückweit heißen die Dinger auch deswegen "statisch". Der Timer wird also nicht generiert. Es sei denn, du meinst damit das Objekt, das den Wert des Feldes durchgehend erhöht.

vor einer Stunde schrieb peachplayer:

werden ja nach dem laden der neuen Szene alle GameObjekte und Variablen gelöscht, oder nicht?

Die GameObjects, die in der Szene sind, werden gelöscht, ja. Aber eben nicht dein statisches Feld. Das existiert schon vor dem Laden der Szene und bleibt auch danach noch erhalten.

Meine Vermutung ist halt, dass du in der zweiten Szene ein Objekt hast, das den Wert des statischen Feldes zurücksetzt. Klar brauchst du zum Weiterzählen auch in der zweiten Szene ein Objekt, das das tut... (wobei man auch mit DontDestroyOnLoad das ursprüngliche behalten könnte... aber das ist schon wieder so ein Thema...) aber dieses Objekt sollte den Wert nicht erstmal wieder auf 0 setzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...