[MELDEN] Von der Vision zum Code: Ein Leitfaden zur Ausrichtung der Geschäftsstrategie auf die Ziele der Softwareentwicklung ist veröffentlicht!
HOL ES DIR HIER

Elektronenbasierte Apps Steam Deck-kompatibel machen

readtime
Last updated on
February 17, 2025

A QUICK SUMMARY – FOR THE BUSY ONES

TABLE OF CONTENTS

Elektronenbasierte Apps Steam Deck-kompatibel machen

Vor einiger Zeit bat uns unser Kunde aus der Spielebranche, Support für das zu implementieren Dampfdeck im Electron-basierten Game Launcher. Unser erster Artikel in der Steam Deck-Serie erklärt die Einzelheiten einer Steam Deck-Kompatibilitätsprüfung und die allgemeinen Anforderungen, die erfüllt werden mussten, um ein gewünschtes Ergebnis zu erhalten verifiziert Status von Valve.

Bis auf ein paar kleinere Fehler funktionierte unsere Anwendung zunächst auf Steam Deck über den Touchscreen. Um jedoch die zu erfüllen Anforderungen, es musste auch die physische Steuerung von Steam Deck unterstützen, ohne dass eine externe Maus oder Tastatur erforderlich war und ohne dass der Benutzer irgendwelche Anwendungseinstellungen ändern musste.

Dieser Artikel konzentriert sich darauf, unsere Erfahrungen mit der Implementierung von Controller-Unterstützung zu teilen und Einblicke in die notwendigen Schritte für diesen Prozess zu geben.

Gamepad-Steuerung zuordnen

Das navigator.getGamePads () Die Methode der Gamepad-API bietet einen detaillierten Einblick in den Zustand der verbundenen Gamecontroller (einschließlich des Steam Decks), insbesondere anhand der Eigenschaftenschaltflächen und Achsen in den einzelnen Controllern. Gamepad Objekt. Der Status einer Taste wird durch die gedrückte Eigenschaft angezeigt (entweder wahr oder falsch) und der Gleitkommawert, der die Intensität des Tastendrucks darstellt. Die Werte im Achsenarray sind Gleitkommazahlen, die die Position entlang der Achse angeben. Andere Werte als 0 bedeuten, dass das Steuerelement gedrückt wurde und die entsprechenden Handler aufgerufen werden sollten.

Um das spezifische, gedrückte Steuerelement zu identifizieren, mussten wir den Status abbilden, der von navigator.getGamePads () Methode zum entsprechenden Gamepad. Ein für den Xbox One-Controller entwickeltes Mapping gewährleistet die Kompatibilität und Konsistenz mit den Steam Deck-Steuerelementen.

Fokussierbare Komponenten

In der Anwendung sind nur bestimmte Komponenten so konzipiert, dass sie fokussierbar sind — insbesondere solche, die ausgelöst werden können, wie Buttons oder Dropdowns. Alle fokussierbaren Elemente mussten identifiziert werden, und die Handler für die unterstützten Steuerelemente wurden für jede fokussierbare Komponente definiert, um auf diese Trigger zu reagieren. Das heißt, wenn zum Beispiel eine Taste A auf dem Steam Deck gedrückt wird, wird eine entsprechende Aktion ausgeführt:

const gamepadButtonHandlers = {
	[buttons.A]: { event: (elementRef) => elementRef.current.click() },
};

Da viele Komponenten dieselben Gamepad-Button-Handler verwendeten (zum Beispiel Taste B, die herkömmlicherweise verwendet wird, um nach oben zu navigieren oder zu einem übergeordneten Menü zurückzukehren), haben wir uns für den gemeinsamen Kontext entschieden.

const GamepadHandlersProvider = ({ children, gamepadButtonHandlers }) => {
	const inheritedContext = useContext(GamepadHandlersContext) || {};

	const newContext = useMemo(
		() => ({
			handlers: {
				...inheritedContext?.handlers,
				...gamepadButtonHandlers,
			},
		}),
		[gamepadButtonHandlers, inheritedContext?.handlers],
	);

	return (
		<GamepadHandlersContext.Provider value={newContext}>
			{children}
		</GamepadHandlersContext.Provider>
	);
};

Gemäß den Steam-Deck-Anforderungen müssen bei Verwendung der physischen Steuerung von Steam Deck die Zeichen auf dem Bildschirm entweder mit den Namen der Steam Deck-Schaltflächen oder den Namen der Xbox 360/One-Tasten übereinstimmen. Um diese Anforderung zu erfüllen, haben wir eine Fußzeile hinzugefügt, die die Steuerelemente und die Statusänderungen enthält, die zu bestimmten Effekten führen, wenn ein bestimmtes Element fokussiert wird.

Zwischen fokussierbaren Komponenten navigieren

Eine der größten Herausforderungen, mit denen wir konfrontiert waren, bestand darin, eine Methode zu entwickeln, um fokussierbare Komponenten zu finden, die sich in der vom Steam Deck D-Pad angegebenen Richtung vom Referenzelement aus befinden. Lassen Sie uns die Entwicklung vom ursprünglichen Konzept zur Lösung dieses Problems bis zur endgültigen Lösung untersuchen.

Zweistufige Verwandte

Wir haben zunächst einen zweistufigen Prozess implementiert: Zuerst betrachteten wir Elemente, die direkt nach rechts, links, oben oder unten ausgerichtet waren, und dann wurden sie auf Elemente in einem 45-Grad-Winkel erweitert, falls keine Übereinstimmung gefunden wurde. Die Ecke des ausgewählten Elements musste innerhalb des angegebenen Bereichs liegen. Im beigefügten Schema wurden die „Ecken“ bestimmter Elemente, die sich in verschiedenen Richtungen vom Referenzelement entfernt befanden, mit einem Kreis markiert. Die Komponente, die dem Referenzelement am nächsten ist, wurde auf der Grundlage ihrer Größe und Position ausgewählt, die aus dem element.getBoundingClientRect () Methode.

Warum blieb dieses Konzept dann hinter den Erwartungen zurück? Obwohl es in der Theorie vielversprechend aussah, war in der Praxis das Element, das ein Benutzer intuitiv als die logischste Wahl identifizieren würde, nicht immer das, was ausgewählt wurde.. Dies galt insbesondere für Gruppen von Elementen, die ähnliche Funktionen erfüllten. Daher mussten wir eine bessere Lösung finden, die nicht nur den Abstand zwischen den Elementen, sondern auch ihre Funktionen berücksichtigte. So kamen wir auf die Idee der Baugruppen..

Komponentengruppen

Um das Konzept der Komponentengruppen zu verstehen, betrachten Sie das folgende Beispiel. Nehmen wir an, wir navigieren nach links. Ohne Gruppierung existieren drei potenziell fokussierbare Komponenten: C1, C2 und C3. Bei einer Gruppierung gibt es nur einen potenziellen Fokus, G1, der als Fokusfänger fungiert. Wenn die Gruppe zuvor nicht fokussiert wurde, wird das erste Element in der Gruppe fokussiert, es sei denn, es ist festgelegt, dass ein anderes Element innerhalb der Gruppe immer zuerst fokussiert wird. Wenn die Gruppe zuvor fokussiert war, wird das zuvor fokussierte Element wieder fokussiert.

Wenn der aktuelle Fokus innerhalb der Gruppe liegt, versucht die Navigation zunächst, sich auf eine Komponente innerhalb der Gruppe zu konzentrieren. Wir versuchen nur dann, von der Gruppe wegzunavigieren, wenn keine Komponente gefunden wird.

Ebenenstapel

Mit der Einführung von Bauteilgruppen wurde das Problem der Navigation zwischen den Komponenten in einer zweidimensionalen Ebene erfolgreich gelöst. Nichtsdestotrotz da unsere Anwendung verschiedene Modals und Dropdowns enthält, mussten wir einen Weg finden, nur Komponenten im aktuellen Kontext zu fokussieren und keine Elemente außerhalb eines bestimmten Bereichs der Anwendung, auch wenn sie auf der Seite gerendert wurden. Dies veranlasste uns, das Konzept eines Anwendungsschichtstapels einzuführen.

Betrachten wir ein Beispiel. Das Öffnen eines Modals von der Homepage aus (L1) sollte den Fokus nur auf die Elemente innerhalb dieses Modals (L2) beschränken. Wenn das Modal ein Dropdown (L3) enthält, sollte das Öffnen des Dropdowns es ermöglichen, sich nur auf die Elemente des Dropdowns zu konzentrieren. Durch Drücken der Taste B im Dropdown sollte das Dropdown geschlossen werden, sodass der Fokus wieder auf das Modal gerichtet ist. Durch erneutes Drücken der Taste B sollte ein Modal geschlossen und der Fokus auf die Startseite als aktuelle Ebene verlagert werden.

Wir konnten dieses Problem lösen, indem wir den Kontext für alle Elemente außerhalb der Hauptebene bereitgestellt und den Ebenenstapel im Anwendungsstatus gespeichert haben. Daher wird jedes Mal, wenn eine Komponente, die zu der neuen Ebene gehört, geöffnet wird, eine Aktion ausgelöst, mit der diese Ebene zum Stapel hinzugefügt wird. Dementsprechend wird beim Schließen einer Komponente die Ebene aus dem Stapel entfernt, sodass die aktive Ebene immer das letzte Element im Stapel der Ebene ist.

useEffect(() => {
	if (enabled) {
		focusDispatch({
			type: “SET_LAYER”,
			payload: layerName,
		});
	}
}, [focusDispatch, layerName, enabled]);

useEffect(() => {
	if (!enabled) {
		focusDispatch({
			type: “POP_LAYER”,
		});
	}
}, [focusDispatch, enabled]);

useEffect(
	() => () => {
		const topLayer = layerStack.slice(-1)[0];
		if (topLayer === layerName) {
			focusDispatch({
				type: “POP_LAYER”,
			});
		}
	},
	[layerStack, focusDispatch, layerName],
);

Debuggen der Anwendung

Bei der Implementierung der Unterstützung für das Steam Deck in unserer Anwendung verwendeten wir PS5-Controller, die an unsere Computer angeschlossen waren, um das Benutzererlebnis zu simulieren. Durch Drücken verschiedener Tasten auf dem Controller haben wir Aktionen wie das Navigieren durch Menüs, das Auswählen von Optionen und das Interagieren mit UI-Elementen repliziert, als ob wir direkt mit dem Steam-Deck interagieren würden. Obwohl die letzten Tests auf dem tatsächlichen Gerät mit dem speziellen Mapping stattfanden, fanden wir diesen Ansatz während der Entwicklung viel praktischer, als direkt auf dem Steam Deck zu arbeiten. Um die Benutzererfahrung bei der Verwendung der Controller zu verbessern, haben wir ein benutzerdefiniertes Tool entwickelt, mit dem der Status aller Steuerelemente auf dem Bildschirm angezeigt wird.

Um eine Steam Deck-Umgebung auf unseren PCs zu simulieren, musste die SteamOS-Umgebungsvariable beim Ausführen der Anwendung auf 1 gesetzt werden.

Ein wertvoller Ausgangspunkt

Die Implementierung der Controller-Unterstützung war sowohl herausfordernd als auch unglaublich interessant. Nichtsdestotrotz Es ist wichtig zu verstehen, dass sich dieser Artikel bewusst auf bestimmte Aspekte konzentriert, anstatt eine erschöpfende Untersuchung aller Nuancen der Controller-Integration zu bieten. Die Herausforderungen, mit denen wir konfrontiert waren, insbesondere die im Zusammenhang mit der Struktur unserer App, sind aufgrund der einzigartigen Einstellungen der verschiedenen Anwendungen möglicherweise nicht allgemein anwendbar. Angesichts des zunehmenden Interesses am Steam Deck könnten immer mehr Softwareentwickler auf die Notwendigkeit stoßen, Controller-Unterstützung in ihren Apps zu implementieren. Aus diesem Grund glauben wir, dass die in diesem Artikel geteilten Erkenntnisse einen wertvollen Ausgangspunkt für die nahtlose Integration von Controllern bieten können.

Frequently Asked Questions

No items found.

Our promise

Every year, Brainhub helps 750,000+ founders, leaders and software engineers make smart tech decisions. We earn that trust by openly sharing our insights based on practical software engineering experience.

Authors

Anna Czekajło-Kozłowska
github
JavaScript-Softwareentwickler

A Fullstack JavaScript Engineer with a strong focus on solving real business problems through code. She's experienced in both web and desktop application development (Electron).

Anna Czekajło-Kozłowska
github
JavaScript-Softwareentwickler

A Fullstack JavaScript Engineer with a strong focus on solving real business problems through code. She's experienced in both web and desktop application development (Electron).

previous article in this collection

No items found.

It's the first one.

next article in this collection

It's the last one.