Jump to content
Unity Insider Forum

Scripten in der Praxis - Der CharacterController


Sascha

Recommended Posts

Dieses Tutorial beschäftigt sich mit der Entwicklung eines Scripts zur Steuerung eines CharacterControllers für ein Jump'n'Run mit 2D-Gameplay.

Kurz gesagt: Links/Rechts/Springen und Schwerkraft.

 

Dieses Tutorial setzt voraus, dass die ersten drei Scripting-Tutorials gelesen wurden, damit die Grundlagen des Scriptens vorhanden sind.

 

Ich benutze weiterhin JavaScript in diesem Tutorial.

 

Anmerkung zum Üben: Die Script-Zwischenstände sind dieses Mal, insbesondere im späteren Teil, meistens in Spoiler-Tags, also versteckt.

Versucht, beim Durcharbeiten des Tutorials, diese Spoiler nicht anzuklicken, sondern selbst zu programmieren.

Geht irgendetwas schief, versucht es zu lösen, klappt das nicht, schaut nach.

Wenn ihr das schafft, trainiert es das Programmieren ohne Skriptbeispiele.

 

Viel Spaß und Erfolg!

 

Scripten in der Praxis - Der CharacterController

Heute widmen wir uns einmal einer Komponente in Unity, die, wenn ein Script dafür erstellt werden soll, vielen Scripting-Einsteigern einen Schauer über den Rücken laufen lässt: Dem CharacterController.

Die Standard-Scripts zur Steuerung dieser Komponente sind zwar ausreichend, wenn man eine Welt baut und diese ingame ansehen will, aber in den meisten Fällen behindern sie einen, das gewünsche Gameplay zu erstellen.

Darum programmieren wir jetzt selbst.

 

Wann benutzen?

Der CharacterController und der Rigidbody sind die einzigen beiden Komponenten, die es ermöglichen, Körper durch den Raum zu bewegen, sodass sie mit anderen Objekten kollidieren können, und nicht einfach hindurch gehen.

Beide haben dabei unterschiedliche Anwendungsfelder, und es ist von der Anwendung abhängig, welche der beiden Komponenten man benutzen sollte.

Faustregel: Der Rigidbody ist für Physikobjekte, sie sich physikalisch korrekt verhalten sollen: Rollende Bälle, fallende Kisten, Fahrzeuge.

Rigidbodys haben Masse und Trägheit imlpementiert und werden jeden FixedFrame von der PhysikEngine beeinflusst, es sei denn, sie "schlafen".

Der CharacterController dagegen ist, wie der Name schon andeutet, für Charaktere, also Menschen konzipiert, ist aber in den meisten Fällen für alle Arten von Lebewesen geeignet.

Ein CharacterController kann nicht gekippt werden, "rollt" nicht "aus" und bewegt oder dreht sich nur, wenn man es will, was parallel steht zu "wenn die Spielfigur es will".

Spielt man also nicht gerade ein Murmel, sondern eine Figur, sollte dieser per CharacterController realisiert sein.

 

Zur Komponente

Jetzt klären wir erst einmal: Was ist der CharacterController überhaupt?

Die Scripting Reference fasst es ganz gut zusammen:

  1. Es ist eine Unterart eines Colliders
     
  2. Er bewegt sich nur, wenn man die Move()-Funktion benutzt
     
  3. Er beachtet dabei Kollisionen

Aus 1. ergibt sich: Ein GameObject kann nur einen CharacterController oder einen normalen Collider haben, weil es immer nur einen Collider pro GameObject geben kann und der CharacterController selbst einer ist.

Ausserdem bedeutet es: Andere Objekte werden mit dem CharacterController kollidieren können.

 

2. bedeutet: Der CharacterController hat keine Gravitation und keine Trägheit. Wenn man ihm sagt, bewege dich drei Meter weit, dann bewegt er sich drei Meter weit. Sagt man es nicht, bewegt er sich nicht.

 

3. schränkt das ein: Kollidiert der CharacterController während das Move()-Aufrufs mit einem anderen Collider, so bewegt er sich nicht weiter in diese Richtung.

 

2. ist allerdings nicht ganz richtig: Man kann ein CharacterController-GameObject auch über seine Transform-Komponente bewegen - in diesem Fall gibt es aber wieder keine Kollisionen!

 

Zusammenfassend müssen wir also nur einen schönen Vektor finden, der sich vermutlich aus dem Input ergibt, und dann Move() mit diesem Vektor aufrufen.

 

Alles bereit

Wir erstellen uns jetzt am besten eine kleine Scene, die wie folgt aussieht:

scene.jpg

Ein paar Cubes, ein Licht, eine Kamera, die eine Rotation von (0,0,0) hat, und eine Kapsel (GameObject => Create Other => Capsule).

Keine Prefabs aus den Standard Assets!

Die Spielwelt erstreckt sich auf der x-Achse, da sich der Controller auf dieser Achse bewegen wird.

Die Kapsel sollte den Boden nicht berühren, da der CharacterController einen kleinen Mindestabstand braucht.

Jetzt die Kapsel markieren und über "Component => Physics => Character Controller" einen CharacterController hinzufügen.

Da die Kapsel schon einen Capsule Collider hat, muss dieser ersetzt werden, bei der erscheinenden Meldung also "Replace" anklicken.

 

Wie man sieht, hat der CharacterController exakt die selbe Form wie der Capsule Collider.

Diese Form ist nämlich für alle aufrecht gehenden Lebewesen gut geeignet und hat auch weitere Vorteile.

Dazu später mehr.

 

Los geht's

Fangen wir jetzt also mit einem neuen Skript an:

function Update()
{

}

Wir fügen eine private Variable hinzu, die die Referenz auf die CharacterController-Komponente halten soll, sodass wir darauf zugreifen können.

Diese Referenz holen wir uns in Awake() mit GetComponent():

private var controller : CharacterController;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{

}

Wir holen uns die Referenz schon am Anfang, damit wir GetComponent() nur einmal und nicht in jedem Frame aufrufen müssen, weil es keine sehr performante Funktion ist.

Wir machen das in Awake() und nicht in Start(), weil Awake() gerade für solche Referenzzuweisungen sind; Start() dagegen ist für erste Aktionen, die z.T. fertige Referenzzuweisungen benötigt.

Wenn z.B. ein anderes Skript beim Start auf "controller" zugreifen wollen würde, würden wir so sicherstellen, dass controller auch schon gesetzt ist, weil alle Start()-Aufrufe nach allen Awake()-Aufrufen ausgeführt werden.

"controller" ist ausserdem private, damit die Variable

  1. nicht im Inspektor auftaucht (warum auch) und
     
  2. nicht von anderen Skripts angefasst werden kann.

Ab jetzt können wir in Update() jederzeit mit "controller" auf unseren CharacterController zugreifen.

 

Damit wir keinesfalls ein null bei GetComponent() bekommen, sorgen wir mit einem netten Trick dafür, dass dieses Skript beim Hinzufügen automatisch einen CharacterController mit hinzufügt:

@script RequireComponent(CharacterController)

Diesen Code, so wie er ist (ohne Semikolon!), ganz an den Anfang (wahlweise auch das Ende...) des Skripts platzieren.

Soll der CharacterController jetzt gelöscht werden, und dieses Script ist auch eine Komponente des Objekts, dann gibt es eine Warnmeldung, dass zuerst die Skriptkomponente entfernt werden soll.

 

So sieht das Script jetzt aus:

 

@script RequireComponent(CharacterController)

private var controller : CharacterController;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{

}

 

 

Den Input-Vektor finden

Jetzt erstellen wir uns in der Update-Funktion einen Vector3 namens dir (für direction), den wir am Ende der Move()-Funktion übergeben werden.

Diesen initialisieren wir mit dem Wert der standardmäßigen horizontalen Input-Achse (links/rechts bzw. A/D) als X-Komponente.

var dir : Vector3 = Vector3(Input.GetAxis("Horizontal"), 0, 0);

Diesen übergeben wir jetzt, multipliziert mit Time.deltaTime, an Move().

controller.Move(dir * Time.deltaTime);

 

Jetzt sieht das Script so aus:

 

@script RequireComponent(CharacterController)

private var controller : CharacterController;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{
var dir : Vector3 = Vector3(Input.GetAxis("Horizontal"), 0, 0);
controller.Move(dir * Time.deltaTime);
}

 

Das Script wird jetzt der Kapsel gegeben.

Zeit zum Testen!

 

Wie man sieht, kann man das Objekt bewegen - es geht nicht durch die Wand, fällt aber auch nicht herunter.

Das ist richtig so - schließlich haben wir die volle Kontroller über die Move-Funktion, und in der steckt noch nichts über Schwerkraft drin.

 

Jetzt fügen wir aber erstmal eine Variable "speed" ein, die für etwas mehr Geschwindigkeit sorgt.

Diese wird mit dem Rückgabewert von GetAxis() multipliziert.

 

@script RequireComponent(CharacterController)

private var controller : CharacterController;
var speed : float = 6;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{
var dir : Vector3 = Vector3(Input.GetAxis("Horizontal") * speed, 0, 0);
controller.Move(dir * Time.deltaTime);
}

 

 

Herunter fallen

Jetzt wollen wir uns um die Schwerkraft kümmern.

Dazu führen wir eine Schwerkraft-Variable ein:

var gravity : float = 9.81;

 

Jetzt wird es Zeit, sich ein wenig Gedanken zu machen.

Im Moment haben wir einen direction-Vektor für Move(), der in jedem Update(), also in jedem Frame, einmal neu erzeugt wird.

Die Geschwindigkeit aus vorherigen Frames sind also nicht vorhanden, da der Vektor am Ende jedes Update()s verloren geht.

Die Fallgeschwindigkeit ist aber abhängig von der letzten Fallgeschwindigkeit, die sich ja konstant erhöht (wir haben ja eine Beschleunigung).

 

Wir müssen also zumindest die y-Komponente von dir als Variable außerhalb des Update()s speichern, um sie in Update() zu verändern und im nächsten Frame wieder zu benutzen.

Wer jetzt ein bisschen weiter denkt oder schon probiert hat, merkt: Meist will man noch z.B. Air Control haben, also eine Beschleunigung nach links und rechts, wenn man in der Luft ist.

Also entschließen wir uns am besten dazu, gleich den ganzen dir-Vektor aus Update() raus zu holen.

@script RequireComponent(CharacterController)

private var controller : CharacterController;
private var dir : Vector3 = Vector3.zero;
var speed : float = 6;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{
controller.Move(dir * Time.deltaTime);
}

dir sollte dabei private sein, da wieder kein Bedarf besteht, im Inspektor an den Werten zu spielen, und auch kein anderes Skript daran rumpfuschen können sollte.

 

Jetzt können wir dir.x und dir.y in Update() beeinflussen.

dir.x = Input.GetAxis("Horizontal") * speed;
dir.y -= gravity * Time.deltaTime; //dir.y um "gravity"m/s senken

 

Jetzt fällt der Charakter herunter, aber etwas stimmt noch nicht.

dir.y sinkt jetzt kontinuierlich, auch, wenn man steht.

Das heißt: Steht man eine Minnute lang irgendwo und geht dann die Kante herunter, fällt man sofort mit einer wahnsinnigen Geschwindigkeit.

Das Problem wird natürlich gelöst, wenn man die Gravitation nur dann wirken lässt, wenn man auch auf dem Boden steht.

 

Ob man auch auf dem Boden steht, ...

...sieht man, wenn controller.isGrounded abgefragt wird.

isGrounded (boolean) ist ein sehr nützlicher Shortcut, der angibt, ob der Controller mit einem Collider unter sich kollidiert ist oder nicht, sprich: Ob er auf dem Boden steht.

Wir benutzen diese Eigenschaft folgendermaßen in Update():

if(controller.isGrounded) //auf dem Boden
{
dir.x = Input.GetAxis("Horizontal") * speed;
}
else //in der Luft
{
dir.y -= gravity * Time.deltaTime;
}

Wie Du siehst: dir.x wird jetzt nur noch neu gesetzt, wenn man auf dem Boden ist, sonst bleibt der Wert wie im letzten Frame.

 

Jetzt ist der Schwerkraft-Feher immernoch nicht ganz behoben: Fällt man, sodass dir.y verändert wird, und landet danach, dann bleibt dir.y danach auf diesem Wert.

Läuft man nochmal von der Kante, fällt man sofort mit dieser Geschwindigkeit weiter.

Also müssen wir den Wert neu setzen, wenn wir auf dem Boden stehen:

dir.y = -1;

Warum -1?

Ganz einfach: Wenn wir nicht ein bisschen nach unten Move()n, wenn wir auf dem Boden sind, kollidieren wir ja nicht mehr damit, und isGrounded gibt false zurück!

Das bedeutet zwar, dass wir im nächsten Frame fallen und somit wieder mit dem Boden kollidieren, aber nur jeden zweiten Frame springen zu können ist ungut, oder?

 

Das vollständige Script bis jetzt:

 

@script RequireComponent(CharacterController)

private var controller : CharacterController;
private var dir : Vector3 = Vector3.zero;
var speed : float = 6;
var gravity : float = 9.81;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{
if(controller.isGrounded) //auf dem Boden
{
	dir.x = Input.GetAxis("Horizontal") * speed;
	dir.y = -1;
}
else //in der Luft
{
	dir.y -= gravity * Time.deltaTime;
}
controller.Move(dir * Time.deltaTime);
}

 

Zeit zum Testen!

Bewege die Kapsel ggf. auf einen der Blöcke, um das Fallen zu testen.

Bei dieser Gelegenheit darf "Gravity" gerne ein wenig angepasst werden, aber bitte gleich über den Inspektor wink.gif

 

Springen!

Jetzt müssen wir nur noch eines: Richtig springen.

Dazu definieren wir uns einfach eine Variable jumpPower vom Typ float, z.B. mit Wert 5.

Wird der "Jump"-Button gedrückt, während man auf dem Boden ist, wird dir.y nicht auf -1, sondern auf jumpPower gesetzt.

Das Skript sieht dann also so aus:

 

@script RequireComponent(CharacterController)

private var controller : CharacterController;
private var dir : Vector3 = Vector3.zero;
var speed : float = 6;
var gravity : float = 9.81;
var jumpPower : float = 5;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{
if(controller.isGrounded) //auf dem Boden
{
	dir.x = Input.GetAxis("Horizontal") * speed;
	if(Input.GetButtonDown("Jump")) //gesprungen?
	{
		dir.y = jumpPower;
	}
	else
	{
		dir.y = -1;
	}
}
else //in der Luft
{
	dir.y -= gravity * Time.deltaTime;
}
controller.Move(dir * Time.deltaTime);
}

 

 

Da es für den Spieler etwas frustrierend sein kann, sekundenlang durch die Luft zu fliegen, während er schon weiß, dass er den Sprung verpatzt hat, bauen wir Air Control ein, damit er sich im Sprung noch retten kann.

Dazu wieder eine neue Variable, die nennen wir airControl, sie ist wieder vom Typ float und hat bei mir erstmal den Wert 15 (Warum höher als speed? Siehst Du gleich).

(Stattdessen kann man z.B. auch airControlFactor einbauen, der mit speed mutlipliziert den selben Zweck erfüllt. Ich bleibe aber beim ersten Ansatz.)

In den Rumpf des else, also, wenn der Spieler in der Luft ist, bauen wir dazu ein, dass dir.x um Input.GetAxis("Horizontal") erhöht (nicht gesetzt) wird, und zwar multipliziert mit Time.deltaTime (weil es eine Beschleunigung ist und damit FPs-abhängig!) und airControl anstatt speed.

 

Das fertige Skript sieht jetzt so aus:

 

@script RequireComponent(CharacterController)

private var controller : CharacterController;
private var dir : Vector3 = Vector3.zero;
var speed : float = 6;
var gravity : float = 9.81;
var jumpPower : float = 5;
var airControl : float = 15;

function Awake()
{
controller = GetComponent(CharacterController);
}

function Update()
{
if(controller.isGrounded) //auf dem Boden
{
	dir.x = Input.GetAxis("Horizontal") * speed;
	if(Input.GetButtonDown("Jump")) //gesprungen?
	{
		dir.y = jumpPower;
	}
	else
	{
		dir.y = -1;
	}
}
else //in der Luft
{
	dir.y -= gravity * Time.deltaTime;
	dir.x += Input.GetAxis("Horizontal") * airControl * Time.deltaTime;
}
controller.Move(dir * Time.deltaTime);
}

 

Wie man sieht, muss airControl mit Time.deltaTime multipliziert werden.

airControl ist daher nicht direkt mit speed zu vergleichen und kann (und sollte, testet es wink.gif ) daher durchaus einen höheren Wert annehmen.

 

Das Skript ist somit erst einmal fertig - man kann laufen, springen und hat sogar Air Control.

Damit also...

 

Zurück zum CharacterController

Eingangs erwähnte ich, dass die Kapselform sehr gut für Menschen und dergleichen geeignet ist.

Warum?

 

Nun, zuerst einmal ist die Kapsel ein Rotationskörper und, da man den CharacterController nicht neigen kann, in diesem Fall um die y-Achse.

Das heisst: Man kann den Controller um die y-Achse drehen, ohne dass sich die Form, von irgendeiner Seite gesehen, ändert.

Ergo kann man beim Drehen um die y-Achse nirgendwo mit kollidieren!

Vergleich mit einem BoxCollider: Dreht man ihn, könnte man mit einer Kante irgendwo anecken.

Für uns bedeutet das jedenfalls, dass man den CharacterController völlig sorglos über transform.Rotate() um die y-Achse drehen kann, ohne fürchten zu müssen, schlaue Spieler könnten sich damit in eine Wand hineindrehen.

Deshalb hat der CharacterController auch keine Rotate()-Funktion.

 

Des Weiteren ist die Kapsel nicht nur ein Zylinder, sondern hat oben und unten je eine Halbkugel.

Das ermöglicht es, die Kapsel Schrägen oder kleine Stufen hinauflaufen zu lassen, ohne dass das als Kollsion mit der Seite erkannt wird.

Die Halbkugelform ermöglicht es sogar, einen genauen Winkel anzugeben, bis zu dem eine Schräge erklommen werden kann (Eigenschaft "Slope Limit").

 

Erweiterungsbeispiele

Dieses Skript kann man natürlich nach blieben mit feinen Raffinessen erweitern.

 

Wie wäre es z.B. mit einem Multijump?

 

Neu einfügen:

var multiJumpCount : int = 3;
private var currentJumpCount : int = 0;

Update() ändern:

function Update()
{
if(controller.isGrounded) //auf dem Boden
{
	dir.x = Input.GetAxis("Horizontal") * speed;
	if(Input.GetButtonDown("Jump")) //gesprungen?
	{
		dir.y = jumpPower;
		currentJumpCount = 1; //Jump Counter zurücksetzen
	}
	else
	{
		dir.y = -1;
	}
}
else //in der Luft
{
	if(currentJumpCount < multiJumpCount && Input.GetButtonDown("Jump")) //darf man noch springen & gesprungen?
	{
		dir.y = jumpPower; //+= wäre auch eine Möglichkeit, oder man könnte eine zweite jumpPower-Variable für in-der-Luft einfügen
		++currentJumpCount; //das gleiche wie currentJumpCount++;
	}
	else
	{
		dir.y -= gravity * Time.deltaTime;
	}
	dir.x += Input.GetAxis("Horizontal") * airControl * Time.deltaTime;
}
controller.Move(dir * Time.deltaTime);
}

 

 

Anstatt einer einfachen Geschwindigkeit könnte man auch ein beschleunigtes Laufen bauen!

 

Neu einfügen:

var acceleration : float = 0.5;

Update() ändern:

	if(controller.isGrounded) //auf dem Boden
{
	dir.x = Mathf.Lerp(dir.x, Input.GetAxis("Horizontal") * speed, acceleration * Time.deltaTime); //an gegebenen Speed-Wert annähern, anstatt zu setzen
	//...

Um den Unterschied zu bemerken, ist ein höherer Speed-Wert sinnvoll.

Acceleration darf durchaus niedriger sein.

Acceleration heisst: Innerhalb einer Sekunde nähern wir uns um das accelearion-fache an den angepeilten Speed-Wert an.

Bei 0.5 heisst das: Nach einer Sekunde 0,5 = 50% Geschwindigkeit,

nach 2 Sekunden 0,5+(Rest*0,5) = 0,5+((1-0,5) * 0,5) = 0,5+(0,5*0,5) = 0,75 = 75% Geschwindigkeit,

usw.

 

 

Fertig

So, jetzt kannst Du einen CharacterController für einen Sidescroller programmieren!

Viel Spaß damit!

 

Anmerkungen und Kritik: Immer her damit.

Sascha Ende.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Jup, nach dem ersten Durchlesen schauts eigentlich gut erklärt und auch verständlich aus. Auch von dem was präsentiert wird, ist der Umfang eig. groß. Somit mal ein "Bowserkoopa gefällt das" :D

Bei mir tun sich die Fragen allerdings immer erst dann auf, wenn ich versuche das Tutorial auf eine eigene Spielfigur anzupassen, also würd ich sagen mach ich das mal ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 3 weeks later...
  • 2 months later...
  • 1 month later...

Hallo finde deine Programmier-Tutorials super.

Würde mich über C# Unity Tutorials freuen.

 

Mal ein paar Anstöße die ich gerne ich gleicher form lesen wollen würde:

  • Wann legt man neue Szenen an?
  • Wie macht man ein Hauptmenü bzw. Hauptmenü-Layer
  • Erste Netzwerkscripte - Server & Clients
  • Erstes Game mit allen Elementen
  • ....

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 1 month later...

Ganz großer Sport deine Tutorials!!!

 

Sind extrem hilfreich für Pixelschubser wie mich da Sie sehr schön anschaulich geschrieben sind.

Auch die geschichte mit dem Spoilen von Scripten finde ich super (ich hab mich nur einmal hinreissen lassen reinzuschauen ;-)

 

Vielen Dank für deine Mühe!!!

 

Naja und falls man sich was Wünschen darf als nächstes Tutorial fänd ich eine Erklärung zu RayCast's super.

Das Ding will einfach nicht in meine rübe rein.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 7 months later...
  • 3 months later...

Hey ich hab ein kleines Problemchen, und zwar wollte ich dass man wenn man 5 Sekundne gelaufen ist man schneller läuft. Klappt aber iwi nicht: (ignoriert die Kommentare einfach)

 

@script RequireComponent(CharacterController) //vergewissert, dass CharacterController-Komponente vorhanden ist

private var controller : CharacterController; //findet den CharacterController und speichert ihn als Variable
private var dir : Vector3 = Vector3.zero; //Variable für Richtung der Bewegung
private var jumpCount : int = 0; //Variable für Doppelsprung; bisher kein Sprung daher 0
private var speedPlus : boolean = false; //Speedbonus erhalten?
var speed : int = 3; //Geschwindigkeit
var gravity : float = 9.81; //Schwerkraft
var jumpPower : float = 5; //Sprunghöhe
var airControl : int = 15; //Korrektur im Sprung


function Awake() //wird vor Update Funktion ausgeführt da controller in Update benötigt wird
{
 controller = GetComponent(CharacterController); //findet den CharacterController uns speichert ihn in der Variable Controller
}

function Update()
{
 if (controller.isGrounded) //checkt ob der Character auf dem Boden steht
 {
   dir.x = Input.GetAxis("Horizontal")*speed; //Bewegung nach links/rechts auf y-Achse
   if (Input.GetAxis("Horizontal") && speedPlus == false) //wenn sich Char bewegt und noch kein Speedbonus erhalten wurde
   {
  speedPlus = true; //Speedbonus wurde erhalten
  yield WaitForSeconds(5); //warte 5 Sekunden (Speedbonus soll nach 5 Sekunden laufen aktiviert werden)
  speed = speed*2; //Geschwindigkeit verdoppelt sich temporär
   }

else

{

speedPlus = false; //Speedbonus wurde nicht erhalten (zum Zurücksetzen der Variable)

}

if (Input.GetButtonDown("Jump"))//wenn Spieler springt

{

dir.y = jumpPower; //Bewegung nach oben in Höhe von jumpPower

jumpCount = 1; //Sprung wird gezählt

}



else

{

dir.y = -1; //Charakter bleibt auf Boden stehen

}

}



else

{

if (jumpCount == 1 && Input.GetButtonDown("Jump")) //Wenn schon gesprungen wurde, aber nur einmal und gesprungen wird

{

dir.y = jumpPower; //charakter springt

jumpCount = 2; //es wurde 2x gesprungen, kein weiterer Sprung nötig

}

else

{

dir.y -= gravity*Time.deltaTime; //Fallbeschleunigung

}

dir.x += Input.GetAxis("Horizontal")*Time.deltaTime*airControl; //Retten des Spielers

}



controller.Move(dir * Time.deltaTime); //Bewegen



}

bearbeitet von Sascha
Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum hast du so viel Leerzeilen da drin, das kann man gar nicht lesen >.<

 

//e: Hab mal ein bisschen Leerzeilen weg gemacht und gesehen, wo dran es liegt: yield gibt's in Update nicht. Du kannst das yield-Gedöns in eine Methode auslagern und sie aufrufen, aber nicht yield in Update benutzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 11 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...