De/Duck Jump Fix

== Einleitung ==
Dieser Artikel wird zeigen, wie Vlaves Implementierung des Duck Jumpings gefixt werden kann. Valves Implementierung (im Orange Box Source SDK) bewegt die Position des Spielers um geschätzt 32 Einheiten in einem Frame nach oben, entlang der positiven Z-Achse. Dies verursacht ein großes Problem im Zusammenhang mit der Vorhersage und Hitbox-Synchronisation zwischen Server und Cleint. Dies verursacht außerdem, dass der Spieler in der Luftschnellt!
Um dieses Problem zu beheben, wird eine Interpolation zum Ducken und Aufstehen in der Luft hinzugefügt. Da nur wenig Zeit für die Interpolation besteht, wird die Duckgeschwindigkeit in der Luft im Gegensatz zur normalen Duckgeschwindigkeit verdoppelt .
== Voraussetzungen ==
* Starker C++-Hintergrund
* Wissen über das Source SDK
* Allgemeines Wissen über die Spielerbewegung
== Der Code ==
Es gibt nicht viel, was programmiert werden muss, es müssen lediglich 2 Funktionen hinzugefügt und 2 Bedingungen angepasst werden. Unten ist ein detailiert Aufschlüsselung jeder Änderung, wodurch diese einfach in die entsprechenden Bereiche eingefügt werden können.
=== GameMovement.h ===
Die folgenden Funktionsdeklarationen müssen zu CGameMovement in GameMovement.h um Zeile 205 hinzugefügt werden:
virtual void	DoDuckJump( float flFraction );
virtual void	DoUnDuckJump( float flFraction );
=== GameMovement.cpp ===
Zuerst muss der Höhenunterschied zwischen geduckt und stehend für den duck Jump definiert werden. Diese Zahl kann beliebig für die eigene Modifikation angepasst werden, da die Höhe des Sprungs evtl. ebenfalls angepasst wurde (nicht Teil des Tutorials. Füge diesen define direkt unter der includes ein:
#define DUCKJUMP_SCALING 0.2f
Als nächstes muss die Funktion befüllt werden, die im Header deklariert wurde (die Kommentare stellen die Beschreibung dar). Die Definitionen können an beliebiger Stelle einegügt werden, eine Nähe zu den anderen Duck-Funktionen sorgt aber für Kontinuität.
void CGameMovement::DoDuckJump( float flFraction )
{
	if ( flFraction >= 1.0f )
	{
		// Da die Duckgeschwindigkeit künstlich beschleunigt wird, wird ausgesagt, dass der Spieler sich duckt, damit er nicht in die Luft fliegt
		player->AddFlag( FL_DUCKING );
		player->m_Local.m_bDucked = true;
		player->m_Local.m_bDucking = false;
		// Erzwingt den ViewOffset als "gänzlich duckend"
		player->SetViewOffset( GetPlayerViewOffset( true ) );
	}
	else
	{
		// Bewegt die Ansicht runter
		SetDuckedEyeOffset( flFraction );
		// Bewegt den Körper einen Bruchteil der Differenz zwischen duckend und stehend nach oben
		Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
		Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
		Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ) * DUCKJUMP_SCALING * flFraction;
		Vector out;
		VectorAdd( mv->GetAbsOrigin(), viewDelta, out );
		mv->SetAbsOrigin( out );
		// Überprüfen, ob der Spieler festhängt
		FixPlayerCrouchStuck( true );
		// Rekategorisieren der Position, da das Ducken die Ursprungskoordinate ändern kann
		CategorizePosition();
	}
}
void CGameMovement::DoUnDuckJump( float flFraction )
{
	if ( flFraction <= 0.0f )
	{
		// Da die Duckgeschwindigkeit künstlich beschleunigt wird, wird ausgesagt, dass der Spieler sich nicht duckt, damit er nicht im Boden versinkt
		player->m_Local.m_bDucked = false;
		player->RemoveFlag( FL_DUCKING );
		player->m_Local.m_bDucking = false;
		player->m_Local.m_bInDuckJump = false;
		// Setzt den ViewOffset auf "gänzlich stehend"
		player->SetViewOffset( GetPlayerViewOffset( false ) );
		player->m_Local.m_flDucktime = 0;
	}
	else
	{
		// Bewegt die Ansicht hoch
		SetDuckedEyeOffset( flFraction );
		// Bewegt den Körper einen Bruchteil der Differenz zwischen duckend und stehend nach unten
		Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
		Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
		Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ) * DUCKJUMP_SCALING * flFraction;
		viewDelta.Negate();
		Vector out;
		VectorAdd( mv->GetAbsOrigin(), viewDelta, out );
		mv->SetAbsOrigin( out );
		// Überprüfen, ob der Spieler festhängt
		FixPlayerCrouchStuck( true );
		// Rekategorisieren der Position, da das Ducken die Ursprungskoordinate ändern kann
		CategorizePosition();
	}
}
Letztendlich wurde die Duck-Funktion eingerichtet, um von den neuen Funktionen zur Interpolation des Spielers beim Ducken verwendet zu werden. Ich kann keine genauen Zeilennummern geben, da meine gamemovement.cpp-Datei stark für unsere eigene Modifikation angepasst ist.
ALTER CODE (DUCKEN):
// Der Spieler befindet sich im Duckübergang und nicht im Ducksprung.
if ( player->m_Local.m_bDucking && !bDuckJump && !bDuckJumpTime )
{
	float flDuckMilliseconds = max( 0.0f, GAMEMOVEMENT_DUCK_TIME - ( float )player->m_Local.m_flDucktime );
	float flDuckSeconds = flDuckMilliseconds * 0.001f;
	
	// beendet den Duckübergang wenn die Überganszeit vorbei ist, wenn "geduckt" oder wenn in der Luft.
	if ( ( flDuckSeconds > TIME_TO_DUCK ) || bInDuck || bInAir )
	{
		FinishDuck();
	}
	else
	{
		// Berechne parametrische Zeit
		float flDuckFraction = SimpleSpline( flDuckSeconds / TIME_TO_DUCK );
		SetDuckedEyeOffset( flDuckFraction );
	}
}
NEUER CODE (DUCKEN):
// Der Spieler befindet sich im Duckübergang und nicht im Ducksprung.
if ( player->m_Local.m_bDucking && !bDuckJump && !bDuckJumpTime )
{
	float flDuckMilliseconds = max( 0.0f, GAMEMOVEMENT_DUCK_TIME - ( float )player->m_Local.m_flDucktime );
	float flDuckSeconds = flDuckMilliseconds * 0.001f;
	
	// beendet den Duckübergang wenn die Überganszeit vorbei ist, wenn "geduckt" oder wenn in der Luft.
	if ( (( flDuckSeconds > TIME_TO_DUCK ) || bInDuck) && !bInAir )
	{
		FinishDuck();
	}
	else if ( bInAir )
	{
		// beschleunige die Duckgeschwindigkeit um das doppelte bei einem Duck Jump
		float flDuckFraction = clamp( SimpleSpline( flDuckSeconds / TIME_TO_DUCK )*2.0f, 0, 1.0f );
		DoDuckJump( flDuckFraction );
	}
	else
	{
		// Berechne parametrische Zeit
		float flDuckFraction = SimpleSpline( flDuckSeconds / TIME_TO_DUCK );
		SetDuckedEyeOffset( flDuckFraction );
	}
}
ALTER CODE (STEHEN):
// Prüfen, ob ein Aufstehen möglich ist.
if ( CanUnduck() )
{
	// oder aufstehen
	if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) )
	{
		float flDuckMilliseconds = max( 0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDucktime );
		float flDuckSeconds = flDuckMilliseconds * 0.001f;
		
		// Beende das Ducken sofort, wenn die Duckzeit vorüber ist oder wenn nicht auf dem Boden
		if ( flDuckSeconds > TIME_TO_UNDUCK || ( bInAir && !bDuckJump ) )
		{
			FinishUnDuck();
		}
		else
		{
			// Berechne parametrische Zeit
			float flDuckFraction = SimpleSpline( 1.0f - ( flDuckSeconds / TIME_TO_UNDUCK ) );
			SetDuckedEyeOffset( flDuckFraction );
			player->m_Local.m_bDucking = true;
		}
	}
}
NEUER CODE (STEHEN):
// Prüfen, ob ein Aufstehen möglich ist.
if ( CanUnduck() )
{
	// oder aufstehen
	if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) )
	{
		float flDuckMilliseconds = max( 0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDucktime );
		float flDuckSeconds = flDuckMilliseconds * 0.001f;
		// Beende das Ducken sofort, wenn die Duckzeit vorüber ist oder wenn nicht auf dem Boden
		if ( flDuckSeconds > TIME_TO_UNDUCK && !bInAir )
		{
			FinishUnDuck();
		}
		else if ( bInAir )
		{
			// Den Vorgang rückgängig machen
			float flDuckFraction = clamp( SimpleSpline( 1.0f - (flDuckSeconds / TIME_TO_UNDUCK) )*2.0f, 0, 1.0f );
			DoUnDuckJump( flDuckFraction );
		}
		else
		{
			// Berechne parametrische Zeit
			float flDuckFraction = SimpleSpline( 1.0f - ( flDuckSeconds / TIME_TO_UNDUCK ) );
			SetDuckedEyeOffset( flDuckFraction );
			player->m_Local.m_bDucking = true;
		}
	}
}
Das ist es. Wenn der Spieler nun springt (bInAir == true), werden die Ansicht und die Position (und die Bounding Box und die Hitboxen) durch DoDuckJump/DoUnDuckJump angepasst und wenn der Spieler auf dem Boden steht, wird die Ansicht durch die normale SetDuckedEyeOffset() angepasst.
== Bemerkungen und zukünftige Arbeiten ==
Unsere Betatester haben berichtet, dass man beim Laufen und Springen an Geschwindigkeit verliert. Das ist für unsere Modifikation kein großes Problem, da unser Ziel das eliminieren des Bunny-Hoppings ist, aber wenn man Bunny Hopping oder CSS-artige Bewegung will, wird diese Implementiweung nicht ohne Anpassung funktionieren.
Ein Weg, um die Geschwindigkeit des Spielers beizubehalten, ist die Berechnung der neuen Ursprungskoordinate und das Hinzufügen eines Bewegungsvektors basierend auf der Geschwindigkeit des Spielers (statt der reinen Z-Achsen-Bewegung).