Unity Cheatsheet¶
Unabhängigkeit - Spielentwicklung für Mobile
Unity ist ein leistungsstarker Cross-Plattform-Spielmotor, der verwendet wird, um 2D, 3D, VR und AR-Spiele und Simulationen zu erstellen. Dieses Cheatsheet konzentriert sich auf mobile Spieleentwicklung mit Unity. < p>
Inhaltsverzeichnis¶
- [Installation](#installation
- [Unity Editor](#unity-editor
- Core Concepts
- Skript mit C#
- [Physik](LINK_4__
- [UI System](#ui-system_
- [Animation](LINK_6__
- [Mobile Entwicklung](#mobile-development
- (#performance-optimization_)
- (#asset-management)
- Test und Debugging
- [Bestellung](LINK_11__
- Beste Praktiken
Installation¶
Unity Hub¶
# Download Unity Hub from unity.com
# Unity Hub manages Unity versions and projects
# Install Unity version
# Open Unity Hub -> Installs -> Add
# Select desired Unity version (e.g., 2021.3 LTS)
# Add modules: Android Build Support, iOS Build Support
# Set environment variables (optional)
# UNITY_HOME=/path/to/unity/version
# PATH=$PATH:$UNITY_HOME/Editor
```_
### Android Setup
```bash
# In Unity Hub, add Android Build Support module
# Unity will install required Android SDK & NDK
# Verify setup in Unity Editor
# Edit -> Preferences -> External Tools
# Check Android SDK, NDK, and JDK paths
# For custom SDK/NDK
# Download from developer.android.com
# Set paths in Unity Editor
```_
### iOS Setup (nur macOS)
```bash
# In Unity Hub, add iOS Build Support module
# Install Xcode from Mac App Store
# Install Xcode Command Line Tools
xcode-select --install
# Install CocoaPods
sudo gem install cocoapods
# Verify setup in Unity Editor
# Edit -> Preferences -> External Tools
# Check Xcode path
```_
## Unity Editor
### Hauptfenster
- **Szene Ansicht**: Visuelle Darstellung der Spielwelt
- **Spielansicht**: Vorschau des Spiels, wie vom Spieler gesehen
- **Hierarchie**: Liste aller GameObjekte in der aktuellen Szene
- **Projekt**: Browser für alle Assets im Projekt
- **Inspector**: Eigenschaften des ausgewählten GameObjects oder Assets
- **Konsole**: Anzeige von Protokollen, Warnungen und Fehlern
### Keyboard Shortcuts
- **Q*: Handwerkzeug (pan)
- **W**: Werkzeug verschieben
- **E**: Drehwerkzeug
- **R**: Skalierwerkzeug
- **T*: Rect Tool (für UI)
- **Ctrl/Cmd + S**: Szene
- **Ctrl/Cmd + P** Spielen/Pause Spiel
- **Ctrl/Cmd + Shift + P**: Schrittrahmen
- **F**: Fokus auf ausgewähltes Objekt
- **Ctrl/Cmd + D**: Duplikat ausgewähltes Objekt
### Projektleitung
```bash
# Create new project
# Unity Hub -> Projects -> New
# Select template (2D, 3D, URP, HDRP)
# Enter project name and location
# Open existing project
# Unity Hub -> Projects -> Add
# Select project folder
# Upgrade project
# Open project in newer Unity version
# Unity will prompt for upgrade
```_
## Kernkonzepte
### GameObject
- Die grundlegenden Objekte in Einheit, die Zeichen, Requisiten, Landschaft, Kameras usw. darstellen.
- Ein Behälter für Komponenten.
### Komponente
- Funktionsstücke eines GameObjects.
- Beispiele: Transform, Mesh Renderer, Rigidbody, Collider, Scripts.
### Transformieren Sie die Komponente
- Jedes GameObject hat eine Transform-Komponente.
- Definiert die Position des GameObject, Rotation und Skala.
### Szene
- Ein Container für einen Satz von GameObjects.
- Repräsentiert eine Ebene, ein Menü oder einen Teil des Spiels.
### Vorarbeit
- Ein wiederverwendbares GameObject in der Projektansicht gespeichert.
- Ermöglicht es Ihnen, ein GameObject mit allen seinen Komponenten, Immobilienwerten und Child GameObjects als wiederverwendbares Gut zu erstellen, zu konfigurieren und zu speichern.
### Forderungen
- Jede Datei, die in einem Unity-Projekt verwendet wird, wie Modelle, Texturen, Sounds, Skripte, etc.
## Skript mit C#
### Erstellen eines Skripts
```csharp
// In Project view, right-click -> Create -> C# Script
// Name the script (e.g., PlayerController)
// Attach script to a GameObject by dragging it to the Inspector
```_
### MonoBehaviour Lebenszyklus
```csharp
using UnityEngine;
public class PlayerController : MonoBehaviour
{
// Called when the script instance is being loaded
void Awake()
{
Debug.Log("Awake called");
}
// Called on the frame when a script is enabled before any of the Update methods are called the first time
void Start()
{
Debug.Log("Start called");
}
// Called every frame
void Update()
{
// Game logic that needs to run every frame
}
// Called every fixed framerate frame
void FixedUpdate()
{
// Physics calculations
}
// Called every frame, after all Update functions have been called
void LateUpdate()
{
// Camera movement, etc.
}
// Called when the object becomes enabled and active
void OnEnable()
{
Debug.Log("OnEnable called");
}
// Called when the object becomes disabled or inactive
void OnDisable()
{
Debug.Log("OnDisable called");
}
// Called when the MonoBehaviour will be destroyed
void OnDestroy()
{
Debug.Log("OnDestroy called");
}
}
```_
### Zugriff auf Komponenten
```csharp
// Get component on the same GameObject
Rigidbody rb = GetComponent<Rigidbody>();
// Get component on a child GameObject
Transform childTransform = GetComponentInChildren<Transform>();
// Get component on a parent GameObject
PlayerController parentController = GetComponentInParent<PlayerController>();
// Add component to GameObject
BoxCollider collider = gameObject.AddComponent<BoxCollider>();
// Find GameObject by name
GameObject player = GameObject.Find("Player");
// Find GameObject by tag
GameObject enemy = GameObject.FindWithTag("Enemy");
// Find all GameObjects with tag
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
```_
### Input Handling
```csharp
// Keyboard input
if (Input.GetKeyDown(KeyCode.Space))
{
// Jump
}
if (Input.GetKey(KeyCode.A))
{
// Move left
}
if (Input.GetKeyUp(KeyCode.LeftShift))
{
// Stop sprinting
}
// Mouse input
if (Input.GetMouseButtonDown(0))
{
// Left mouse button clicked
}
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
// Touch input
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
Debug.Log("Touch began");
}
if (touch.phase == TouchPhase.Moved)
{
Debug.Log("Touch moved");
}
if (touch.phase == TouchPhase.Ended)
{
Debug.Log("Touch ended");
}
}
```_
### Coroutinen
```csharp
using System.Collections;
using UnityEngine;
public class CoroutineExample : MonoBehaviour
{
void Start()
{
StartCoroutine(Fade());
}
IEnumerator Fade()
{
Debug.Log("Coroutine started");
// Wait for 2 seconds
yield return new WaitForSeconds(2f);
Debug.Log("After 2 seconds");
// Wait for the end of the frame
yield return new WaitForEndOfFrame();
Debug.Log("End of frame");
// Wait for another coroutine to finish
yield return StartCoroutine(AnotherCoroutine());
Debug.Log("Coroutine finished");
}
IEnumerator AnotherCoroutine()
{
Debug.Log("Another coroutine started");
yield return new WaitForSeconds(1f);
Debug.Log("Another coroutine finished");
}
// Stop a coroutine
public void StopMyCoroutine()
{
StopCoroutine(Fade());
StopAllCoroutines();
}
}
```_
### Geschäftsführung
```csharp
using UnityEngine.SceneManagement;
// Load a scene by name
SceneManager.LoadScene("Level1");
// Load a scene by index
SceneManager.LoadScene(1);
// Load a scene asynchronously
StartCoroutine(LoadSceneAsync("Level2"));
IEnumerator LoadSceneAsync(string sceneName)
{
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
while (!asyncLoad.isDone)
{
float progress = Mathf.Clamp01(asyncLoad.progress / 0.9f);
Debug.Log("Loading progress: " + (progress * 100) + "%");
yield return null;
}
}
// Get the current scene
Scene currentScene = SceneManager.GetActiveScene();
string sceneName = currentScene.name;
```_
## Physik
### Rigidbody
- Komponenten, die ein GameObject von der Physik beeinflusst werden können.
- **Rigidbody**: Für 3D Physik.
- **Rigidbody2D*: Für 2D Physik.
```csharp
// Get Rigidbody component
Rigidbody rb = GetComponent<Rigidbody>();
// Add force
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
// Add torque (rotation)
rb.AddTorque(Vector3.up * 5f, ForceMode.Force);
// Set velocity
rb.velocity = new Vector3(0, 10, 0);
// Move position (for kinematic rigidbodies)
rb.MovePosition(transform.position + Vector3.forward * Time.deltaTime);
// Move rotation
rb.MoveRotation(transform.rotation * Quaternion.Euler(Vector3.up * 10f * Time.deltaTime));
```_
### Collier
- Definiert die Form eines GameObjects für physische Kollisionen.
- **BoxCollider**, **SphereCollider**, **CapsuleCollider**, **MeshCollider***.
- **BoxCollider 2D**, **CircleCollider2D**, **CapsuleCollider2D***.
### Kollisionserkennung
```csharp
// Called when this collider/rigidbody has begun touching another rigidbody/collider
void OnCollisionEnter(Collision collision)
{
Debug.Log("Collision entered with: " + collision.gameObject.name);
if (collision.gameObject.CompareTag("Enemy"))
{
// Take damage
}
}
// Called once per frame for every collider/rigidbody that is touching another rigidbody/collider
void OnCollisionStay(Collision collision)
{
Debug.Log("Collision staying with: " + collision.gameObject.name);
}
// Called when this collider/rigidbody has stopped touching another rigidbody/collider
void OnCollisionExit(Collision collision)
{
Debug.Log("Collision exited with: " + collision.gameObject.name);
}
```_
### Trigger-Detektion
- Collider können als "Is Trigger" markiert werden, um zu erkennen, wann Objekte ihr Volumen eingeben, ohne eine Kollision zu verursachen.
```csharp
// Called when the Collider other enters the trigger
void OnTriggerEnter(Collider other)
{
Debug.Log("Trigger entered by: " + other.gameObject.name);
if (other.CompareTag("Player"))
{
// Collect item
}
}
// Called once per frame for every Collider other that is touching the trigger
void OnTriggerStay(Collider other)
{
Debug.Log("Trigger staying with: " + other.gameObject.name);
}
// Called when the Collider other has stopped touching the trigger
void OnTriggerExit(Collider other)
{
Debug.Log("Trigger exited by: " + other.gameObject.name);
}
```_
### Raycasting
```csharp
// Cast a ray from the camera to the mouse position
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f))
{
Debug.Log("Ray hit: " + hit.collider.gameObject.name);
Debug.Log("Hit point: " + hit.point);
}
// Cast a ray from a transform
if (Physics.Raycast(transform.position, transform.forward, out hit, 10f))
{
Debug.Log("Forward ray hit: " + hit.collider.gameObject.name);
}
// Raycast with layer mask
int layerMask = 1 << 8; // Layer 8
if (Physics.Raycast(transform.position, Vector3.down, out hit, 2f, layerMask))
{
Debug.Log("Ground detected");
}
```_
## UI System
### ~
- Das Wurzelelement für alle UI in einer Szene.
- **Render Mode**: Screen Space - Overlay, Screen Space - Kamera, Weltraum.
### UI Komponenten
- **Text**: Zeigt Text an.
- **Bild**: Zeigt ein Bild an.
- **RawImage**: Zeigt eine Textur an.
- **Button**: Eine klickbare Taste.
- **Toggle**: Eine Checkbox.
- **Slider**: Ein schleppbarer Schieber.
- **Scrollbar**: Eine Scrollbar.
- **InputField*: Ein Texteingangsfeld.
- **Panel**: Ein Container für andere UI Elemente.
- **Scroll Ansicht**: Eine skalierbare Ansicht.
### Rekrutentransform
- Die Transform-Komponente für UI-Elemente.
- Definiert Position, Größe, Anker und Schwenk.
### Veranstaltungssystem
- Zeigt Eingabeereignisse für UI-Elemente an.
- Erfordert ein **EventSystem** GameObject in der Szene.
### Klicken Sie auf Event
```csharp
using UnityEngine.UI;
public Button myButton;
void Start()
{
myButton.onClick.AddListener(OnButtonClick);
}
void OnButtonClick()
{
Debug.Log("Button clicked!");
}
```_
### Zugriff auf UI-Komponenten
```csharp
using UnityEngine.UI;
public Text scoreText;
public Image healthBar;
public InputField nameInput;
void UpdateScore(int score)
{
scoreText.text = "Score: " + score;
}
void UpdateHealth(float health)
{
healthBar.fillAmount = health / 100f;
}
string GetPlayerName()
{
return nameInput.text;
}
```_
## Animation
### Animator-Komponente
- Kontrolliert Animationen auf einem GameObject.
- Verwendet einen **Animator Controller** Asset, um Animationszustände zu verwalten.
### Animator Controller
- Eine staatliche Maschine für Animationen.
- **States**: Repräsentiere individuelle Animationen (z.B. Idle, Walk, Run, Jump).
- **Transitionen**: Definieren, wie man sich zwischen Staaten bewegt.
- **Parameter*: Variablen, die Übergänge steuern (z.B. Speed, IsJumping).
### Animation Clips
- Aktiva, die Animationsdaten enthalten (z.B. Keyframes für Position, Rotation, Skala).
### Steuerung von Animationen von Script
```csharp
Animator animator = GetComponent<Animator>();
// Set float parameter
animator.SetFloat("Speed", moveSpeed);
// Set bool parameter
animator.SetBool("IsJumping", true);
// Set trigger parameter
animator.SetTrigger("Attack");
// Get current animation state
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
if (stateInfo.IsName("Attack"))
{
// In attack animation
}
```_
### Veranstaltungen
- Rufen Sie eine Funktion an einem bestimmten Punkt in einem Animationsclip an.
- Fügen Sie eine Veranstaltung im Fenster Animation hinzu.
```csharp
// Function to be called by animation event
public void OnAttackAnimationEnd()
{
Debug.Log("Attack animation finished");
}
```_
## Mobile Entwicklung
### Platform-Specific Compilation
```csharp
#if UNITY_ANDROID
// Android-specific code
#elif UNITY_IOS
// iOS-specific code
#else
// Code for other platforms (e.g., Editor)
#endif
```_
### Bildschirmorientierung
```csharp
// Set screen orientation
Screen.orientation = ScreenOrientation.LandscapeLeft;
// Allow auto-rotation
Screen.autorotateToPortrait = true;
Screen.autorotateToLandscapeLeft = true;
Screen.autorotateToLandscapeRight = true;
Screen.autorotateToPortraitUpsideDown = false;
```_
### Touch Controls
```csharp
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Began:
// Handle touch began
break;
case TouchPhase.Moved:
// Handle touch moved
Vector2 touchDeltaPosition = touch.deltaPosition;
break;
case TouchPhase.Ended:
// Handle touch ended
break;
}
}
}
```_
### Beschleunigungsmesser
```csharp
// Get accelerometer data
Vector3 acceleration = Input.acceleration;
// Use accelerometer for movement
float moveHorizontal = acceleration.x;
float moveVertical = acceleration.y;
```_
### Gyroskop
```csharp
// Enable gyroscope
Input.gyro.enabled = true;
// Get gyroscope data
Quaternion rotation = Input.gyro.attitude;
Vector3 rotationRate = Input.gyro.rotationRate;
```_
### Geräte Vibration
```csharp
// Vibrate device
Handheld.Vibrate();
```_
### Dauerhafte Daten
```csharp
// PlayerPrefs for simple data storage
PlayerPrefs.SetInt("HighScore", 100);
PlayerPrefs.SetString("PlayerName", "John");
PlayerPrefs.SetFloat("Volume", 0.8f);
int highScore = PlayerPrefs.GetInt("HighScore", 0);
string playerName = PlayerPrefs.GetString("PlayerName", "Guest");
float volume = PlayerPrefs.GetFloat("Volume", 1.0f);
PlayerPrefs.Save();
// For complex data, use file I/O
string path = Application.persistentDataPath + "/save.dat";
File.WriteAllText(path, "My save data");
string saveData = File.ReadAllText(path);
```_
## Leistungsoptimierung
### Profil
- Ein Werkzeug zur Analyse und Optimierung der Spielleistung.
- **Window -> Analyse -> Profiler**.
- **CPU Verwendung**: Kennzeichnen von Leistungsengpässen in Skripten.
- **GPU-Nutzung**: Analyse der Rendering-Performance.
- **Memory**: Speicherzuordnungen verfolgen und Speicherlecks identifizieren.
### Batch
- **Static Batching**: Für nicht bewegte Objekte, die dasselbe Material teilen.
- **Dynamic Batching**: Für kleine bewegte Objekte, die das gleiche Material teilen.
### Kulling
- **Frustum Culling*: automatisch aktiviert. Objekte außerhalb der Kameraansicht werden nicht dargestellt.
- **Occlusion Culling*: Verhindert das Rendern von Objekten, die hinter anderen Objekten versteckt sind.
### Detailtiefe (LOD)
- Renders Modelle mit verschiedenen Detailebenen basierend auf ihrer Entfernung von der Kamera.
### Textur Kompression
- Verwenden Sie für jede Plattform geeignete Textur-Kompressionsformate (z.B. ASTC für Android/iOS).
### Objekt Pooling
- Wiederverwenden von Objekten statt sie oft zu lösen und zu zerstören.
```csharp
public class ObjectPool : MonoBehaviour
{
public GameObject objectToPool;
public int amountToPool;
private List<GameObject> pooledObjects;
void Start()
{
pooledObjects = new List<GameObject>();
for (int i = 0; i < amountToPool; i++)
{
GameObject obj = Instantiate(objectToPool);
obj.SetActive(false);
pooledObjects.Add(obj);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObjects.Count; i++)
{
if (!pooledObjects[i].activeInHierarchy)
{
return pooledObjects[i];
}
}
return null;
}
}
```_
## Vermögensverwaltung
### AssetBunds
- Archiv der Vermögenswerte, die auf Anfrage geladen werden können.
- Verwendet für Download-Inhalte (DLC) und reduziert die anfängliche App-Größe.
### Adressierbare Vermögenswerte
- Ein System, um Assets per Adresse zu verwalten und zu laden.
- Vereinfacht das Asset Management und das Laden.
### Ressourcen-Ordner
- Ein spezieller Ordner, in dem Vermögenswerte mit Namen zur Laufzeit geladen werden können.
- Nicht empfohlen für große Projekte aufgrund von Leistungsbeeinträchtigungen.
```csharp
// Load asset from Resources folder
Texture2D texture = Resources.Load<Texture2D>("Textures/my_texture");
GameObject prefab = Resources.Load<GameObject>("Prefabs/my_prefab");
```_
## Prüfung und Debugging
### Debugging
```csharp
// Log messages to the console
Debug.Log("This is a log message");
Debug.LogWarning("This is a warning");
Debug.LogError("This is an error");
// Draw debug lines in the Scene view
Debug.DrawLine(transform.position, transform.position + transform.forward * 10f, Color.red);
Debug.DrawRay(transform.position, transform.forward * 10f, Color.green);
// Assertions
Debug.Assert(condition, "Assertion failed");
```_
### Unity Test Framework
- Ein Rahmen für das Schreiben und Führen von automatisierten Tests in Unity.
- **Window -> Allgemeines -> Test Runner**.
- ** Modustests bearbeiten*: Im Unity Editor laufen.
- **Play Mode Tests*: Laufen Sie im Spiel, während es spielt.
```csharp
// Edit Mode Test
using NUnit.Framework;
public class CalculatorTests
{
[Test]
public void Add_TwoNumbers_ReturnsSum()
{
var calculator = new Calculator();
var result = calculator.Add(2, 3);
Assert.AreEqual(5, result);
}
}
// Play Mode Test
using System.Collections;
using NUnit.Framework;
using UnityEngine.TestTools;
public class PlayerTests
{
[UnityTest]
public IEnumerator Player_Jumps_ChangesYPosition()
{
var player = new GameObject().AddComponent<Player>();
float initialY = player.transform.position.y;
player.Jump();
yield return new WaitForSeconds(0.5f);
Assert.Greater(player.transform.position.y, initialY);
}
}
```_
## Bereitstellung
### Einstellungen erstellen
- **File -> Einstellungen* erstellen.
- Wählen Sie die Zielplattform (Android, iOS).
- Fügen Sie Szenen zum Build hinzu.
- Konfigurieren Sie die Playereinstellungen.
### Spielereinstellungen
- **Bearbeiten -> Projekteinstellungen -> Spieler**.
- **Firmenname** **Produktname**, **Version**.
- **Icon***, **Splash Screen***.
- **Bundle Identifier** (z.B. Com.company.product).
- ** Skripting Backend** (Mono, IL2CPP).
- **API Compatibility Level**.
### Android Build
```bash
# In Build Settings, switch to Android platform
# Connect Android device with USB debugging enabled
# Click "Build and Run"
# To create an APK
# Click "Build"
# Save the APK file
# To create an AAB (Android App Bundle)
# Check "Build App Bundle (Google Play)"
# Click "Build"
```_
### iOS Build (nur macOS)
```bash
# In Build Settings, switch to iOS platform
# Click "Build"
# This will generate an Xcode project
# Open the Xcode project
# In Xcode, set up signing and capabilities
# Select target device and run the app
# To create an archive for App Store
# In Xcode, Product -> Archive
```_
## Best Practices
### Projektorganisation
Coding Practices¶
- Verwenden Sie Namensräume, um Code zu organisieren.
- Folgen Sie einer konsistenten Namenskonvention (z.B. PascalCase für Klassen, camelCase für Variablen).
- Cache-Komponenten-Referenzen in
AwakeoderStart. - Vermeiden Sie die Verwendung
GameObject.FindinUpdate_. - Verwenden Sie Objektpooling für häufig erstellte Objekte.
- Optimieren Sie Schleifen und vermeiden Sie unnötige Berechnungen.
Leistungsspitzen¶
- Verwenden Sie den Profiler, um Engpässe zu identifizieren.
- Verwenden Sie statische Chargen für statische Objekte.
- Verwenden Sie LOD für komplexe Modelle.
- Komprimieren Sie Texturen und Audio.
- Verwenden Sie entsprechende Shader und vermeiden Sie Overdraw.
- Halten Sie die Hierarchie so flach wie möglich.
--
Zusammenfassung¶
Unity ist eine vielseitige und leistungsstarke Spielmaschine für mobile Entwicklung, bietet eine reiche Reihe von Werkzeugen und Funktionen, um hochwertige Spiele und interaktive Erfahrungen zu schaffen.
Key Vorteile: - Cross-Platform*: Erstellen Sie für Android, iOS und andere Plattformen aus einer einzigen Codebase. - **Rich Ecosystem*: Großer Asset Store mit gebrauchsfertigen Vermögenswerten, Werkzeugen und Erweiterungen. - **Powerful Editor: Intuitiver und flexibler Editor für Design- und Gebäudespiele. - Strong Community: Umfangreiche Dokumentation, Tutorials und Unterstützung der Gemeinschaft.
Best Use Cases: - 2D und 3D mobile Spiele aller Genres. - AR und VR Anwendungen. - Interaktive Simulationen und Visualisierungen. - Rapid Prototyping und Entwicklung.
Verbleib: - Kann eine steile Lernkurve für Anfänger haben. - Leistungsoptimierung ist für mobile Plattformen entscheidend. - Baugröße kann groß sein, wenn nicht sorgfältig verwaltet.
Durch das Mastering Unity's Kernkonzepte, Skripting und mobile spezifische Features können Entwickler ansprechende und erfolgreiche mobile Spiele erstellen.
<= <= <= <================================================================================= Funktion copyToClipboard() {\cHFFFF} const commands = document.querySelectorAll("code"); alle Befehle = "; Befehle. Für jede(cmd) => alle Befehle += cmd.textContent + "\n"); navigator.clipboard.writeText (allCommands); alarm("Alle Befehle in der Zwischenablage kopiert!"); }
Funktion generierenPDF() { Fenster.print(); }