MINT lernen

C#: Spiele und Anwendungen

Zwei Textaufgaben mit Hinweisen und Erwartungshorizont zu C#-Programmen und Unity-Skripten.

Dein Fortschritt:
0 / 0 Aufgaben
1

Der Sparplan

AFB I–II

Jonas spart für Kopfhörer, die 60 € kosten. Er bekommt jede Woche 7 € Taschengeld und legt alles zur Seite. Seine Schwester hat ihm dafür ein kleines C#-Konsolenprogramm geschrieben:

C#
using System;

class Sparplan
{
    static void Main()
    {
        int wochengeld = 7;
        int ziel = 60;
        int erspart = 0;
        int wochen = 0;
        while (erspart < ziel)
        {
            erspart = erspart + wochengeld;
            wochen++;
        }
        Console.WriteLine("Ziel nach " + wochen + " Wochen erreicht.");
        Console.WriteLine("Erspart: " + erspart + " Euro");
    }
}
  1. Gib die beiden Zeilen an, die das Programm in der Konsole ausgibt.
  2. Beschreibe, welche Schritte der Quelltext durchläuft, bis er auf dem Computer ausgeführt wird. Verwende die Begriffe CIL, .NET und JIT-Compiler.
  3. Jonas ergänzt vor der Schleife die Zeile erspart = 2.5;. Erkläre, warum das Programm dann überhaupt nicht startet — und nicht erst in dieser Zeile abbricht.

Hinweise

Hinweis zu Aufgabe a)
Spiele die Schleife mit einer kleinen Tabelle durch: erspart und wochen nach jedem Durchlauf. Wann ist erspart < ziel zum ersten Mal falsch?
Hinweis zu Aufgabe b)
Es wird zweimal übersetzt — einmal vor dem Start und einmal während des Laufs.
Hinweis zu Aufgabe c)
Welchen Typ hat erspart? Wer prüft Typen in C#, und wann?

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

erspart: 7, 14, 21, 28, 35, 42, 49, 56, 63. Nach 8 Wochen sind es 56 € < 60 €, erst nach der 9. Woche ist die Bedingung falsch. Ausgabe:

Ziel nach 9 Wochen erreicht.
Erspart: 63 Euro

Erwartungshorizont zu Aufgabe b)

Der C#-Compiler prüft den Quelltext (Syntax, Typen) und übersetzt ihn in die Zwischensprache CIL, die in einer Programmdatei gespeichert wird. Beim Start lädt die .NET-Laufzeitumgebung die CIL; ein JIT-Compiler übersetzt die Methoden während des Laufs in Maschinencode für den jeweiligen Prozessor, der ihn dann ausführt.

Erwartungshorizont zu Aufgabe c)

erspart ist als int deklariert, 2.5 ist aber eine Kommazahl (double). C# ist statisch typisiert: Der Compiler prüft alle Typen schon vor dem Start und meldet einen Fehler („double kann nicht implizit in int umgewandelt werden“). Weil ohne fehlerfreie Übersetzung keine CIL entsteht, gibt es gar kein Programm, das starten könnte — anders als bei einem Interpreter wie Python, der erst beim Erreichen der Zeile abbricht.

2

Der platzende Ballon

AFB II–III

Mira programmiert für den Tag der offenen Tür ein Geschicklichkeitsspiel in Unity. Ein Ballon soll genau 10 Sekunden nach Spielbeginn platzen (Destroy(gameObject) entfernt ihn aus dem Spiel). Auf ihrem Laptop läuft das Spiel mit 60 fps, und dort klappt alles. Ihr Skript:

C#
using UnityEngine;

public class Ballon : MonoBehaviour
{
    float zeit;
    int frames;

    void Start()
    {
        zeit = 0f;
        frames = 0;
        Debug.Log("Ballon startet");
    }

    void Update()
    {
        frames++;
        zeit += Time.deltaTime;
        if (frames >= 600)
        {
            Destroy(gameObject);
        }
    }
}
Ablauf eines Unity-Skripts
Start() einmal Update() Update() Update() Update() Frame 1 Frame 2 Frame 3 Frame 4 Zeit Spielstart Δt = Time.deltaTime
Start() läuft einmal, Update() in jedem Frame. Der zeitliche Abstand Δt zwischen zwei Frames steht in Time.deltaTime.
  1. Erläutere mithilfe der Abbildung, wie oft die Meldung „Ballon startet“ erscheint und wie oft Update() in den ersten 10 Sekunden auf Miras Laptop aufgerufen wird.
  2. Ermittle, nach wie vielen Sekunden der Ballon auf einem Tablet mit 30 fps und auf einem Gaming-PC mit 120 fps platzt.
  3. Verändere das Skript so, dass der Ballon auf jedem Gerät nach 10 Sekunden platzt. Gib die geänderten Zeilen an.
  4. Ihr Mitschüler meint: „Einfacher: Stell die Bildrate fest auf 60 fps ein, dann stimmt die Zählung mit frames immer.“ Beurteile diesen Vorschlag.

Hinweise

Hinweis zu Aufgabe a)
Start() läuft einmal, Update() einmal pro Frame. Wie viele Frames gibt es in 10 s bei 60 fps?
Hinweis zu Aufgabe b)
Der Ballon platzt nach 600 Frames. Teile die Framezahl durch die Bildrate.
Hinweis zu Aufgabe c)
Eine Variable im Skript wird zwar hochgezählt, aber nie benutzt — welche, und was steht in ihr?
Hinweis zu Aufgabe d)
Denk an Geräte, die 60 fps gar nicht schaffen, und an Frames, die unterschiedlich lange dauern.

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

„Ballon startet“ steht in Start(), das Unity genau einmal vor dem ersten Frame aufruft — die Meldung erscheint also einmal. Update() wird in jedem Frame aufgerufen: bei 60 fps in 10 s etwa 60 · 10 = 600-mal. Deshalb platzt der Ballon auf Miras Laptop tatsächlich nach 10 s.

Erwartungshorizont zu Aufgabe b)

Der Ballon platzt immer nach 600 Frames. Tablet: 600 : 30 = 20 s. Gaming-PC: 600 : 120 = 5 s. Die Wartezeit hängt also von der Bildrate ab — auf dem schnellen PC platzt er doppelt so früh, auf dem Tablet doppelt so spät.

Erwartungshorizont zu Aufgabe c)

Statt der Frames die vergangene Zeit prüfen, die ohnehin in zeit aufsummiert wird:

if (zeit >= 10f)
{ Destroy(gameObject); }

Die Zeilen mit frames können entfallen. Weil Time.deltaTime die echte Dauer jedes Frames enthält, ergibt die Summe nach 10 s immer etwa 10 — egal wie viele Frames es waren.

Erwartungshorizont zu Aufgabe d)

Der Vorschlag löst das Problem nur scheinbar: Eine feste Bildrate ist nur eine Obergrenze. Schafft ein schwaches Gerät keine 60 fps oder ruckelt das Spiel kurz, laufen weniger Frames und der Ballon platzt zu spät. Außerdem verschenkt man auf schnellen Geräten flüssigere Bilder. Die Lösung mit Time.deltaTime ist robuster und genauso einfach — sie ist deshalb vorzuziehen. (Vollständig ist eine Antwort, die mindestens ein Gegenargument nennt und zu einem begründeten Urteil kommt.)