Jump to content
Unity Insider Forum

RPG Basics - Abilities - Teil 1


AgentCodeMonk

Recommended Posts

Hallo und willkommen zur zweiten Reihe einer kleinen Tutorialreihe rund um das Thema

RPG. In diesem Teil möchte ich euch demonstrieren wie man seinem Character Fähigkeiten gibt.

Dies soll als Basis für ein Kampfsystem dienen. Im Rahmen des Tutorials und der Tatsache, das dieses Gebiet komplex werden kann, wird dieses AbilitySystem natürlich nicht riesig werden und nur die grundlegende Herangehensweise demonstrieren.

Jedoch wird es so angelegt, dass es nach oben skalierbar lässt!

Dieses Tutorial richtet sich eher an Anfänger als an Fortgeschrittene und natürlich auch an Diejenigen, die solche Systeme schon immer interessiert haben. Wie immer ist das Ganze eine von vielen Möglichkeiten ein AbilitySystem umzusetzen.

 

 

Legen wir los und definieren erstmal die Fähigkeit. Dafür legen wir uns eine Klasse an:

 

public class Ability
{

}

 

Soweit so gut, wir haben eine Fähigkeit. Das ging schnell...wir sind fertig ;) nene...

Diese Fähigkeit wird uns nun als Basis für alle anderen Fähigkeiten dienen, die einem so einfallen könnten.

 

Damit wir mit dieser Fähigkeit etwas anfangen können, bekommt sie einige Parameter und wir nehmen an, dass diese Fähigkeit Schaden am Gegner verursachen soll.

Ganz im Stile der MMO´s geben wir den Fähigkeiten Abklingzeiten, bzw "CoolDowns", die bestimmen in welchem Interval die Fähigkeit benutzt werden kann.

 

public class Ability
{
public string _name = "Attack";
public int _cost = 0; // Resourcenverbrauch (Mana,  etc)
public float _coolDown = 1.5f; // je nach Fähigkeit unterschiedlich
public float _coolDownTimer = 0f; // ein timer...
public float _damageModifier = 1f; // der Wert mit dem der Schadenswert verrechnet wird.
//... type, icon, class etc - hier kann es noch einiges geben um die Fähigkeit näher zu definieren, zb welche Klasse kann sie benutzen oder macht die Fähigkeit magischen oder physischen Schaden. enums bieten sich an!
}

 

Um Fähigkeiten zu nutzen, müssen sie natürlich irgendwie ins Spiel gelangen.

Dazu nutze ich gerne Dictionaries. Sicher mag jetzt der ein oder andere sagen, das man ja auch Lists nutzen kann oder ArrayLists.

 

Ich entscheide mich jedoch definitiv für das Dict und lasse es folgendermaßen aussehen:

 

public Dictionary<string, Ability> ABILITIES = new Dictionary<string, Ability>();

 

Zum Nachlesen:

http://www.dotnetperls.com/dictionary

 

Dieses Dict kann man jetzt einfach zu seinem CharacterScript hinzufügen ODER wir machen ein neues und nennen es Player.cs

 

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Player : MonoBehaviour
{
#region VARIABLES
public Dictionary<string, Ability> ABILITIES = new Dictionary<string, Ability>();
//... etc
#endregion
}

 

Wichtig: using System.Collections.Generic; ! Sonst wird´s nichts mit dem Dict;)

 

Der Grund Dictionaries zu verwenden ist einfach der, dass sich auch später wesentlich besser nachvollziehen lässt welche Fähigkeit

gerade wohin geschickt wird etc. alles läuft anhand des Names der Fähigkeit (anstatt einer ID), so dass auch nach wochen noch klar ist,

was eine Fähigkeit ist, warum sie so heißt. Ich für meinen Teil kann mit der Fähigkeit "Mortal Strike" mehr anfangen als mit der id 6534651 ;)

Davon abgesehen nutzen wir eh eine recht übersichtliche Anzahl von Fähigkeiten und nicht 100000.

 

Um nun endlich die Fähigkeit verfügbar zu machen, muss sie noch ins Dict, das machen wir in der Start Methode.

 

 

public class Player : MonoBehaviour
{
#region VARIABLES
public Dictionary<string, Ability> ABILITIES = new Dictionary<string, Ability>();
//... etc
#endregion

void Start()
{
	Ability _ability = new Ability();
	ABILITIES.Add(_ability._name, _ability);
}
}

 

Damit das noch ein wenig flexibler wird:

public class Player : MonoBehaviour
{
#region VARIABLES
public Dictionary<string, Ability> ABILITIES = new Dictionary<string, Ability>();
//... etc
#endregion

void Start()
{
	Ability _ability = new Ability();
	AddAbility(_ability);
}

void AddAbility(Ability _ability)
{
	ABILITIES.Add(_ability._name, _ability);
}
}

Damit es noch etwas schicker wird geben wir der Ability einen Konstruktor mit einigen überladungen mit.

public class Ability
{

public Ability(string _n, float _dn)
{
	this._name = _n;
	this._damageModifier = _dn;
}

public string _name = "Attack";
public int _cost = 0;
public float _coolDown = 1.5f;
public float _coolDownTimer = 0f;
public float _damageModifier = 1f;
//... type, icon, class etc
}

 

Das sollte reichen für´s Erste, später gibt es hier noch einiges umzustellen, also nicht voreilig werden;)

 

Jetzt schauen wir nochmal in die Start Methode der Player.cs


void Start()
{
	Ability _ability = new Ability("Mortal Strike", 1.3f);
	AddAbility(_ability);
}

 

Wir können nun natürlich auch mehrere Fähigkeiten on the fly erstellen:

void Start()
{
	Ability _ability = new Ability("Mortal Strike", 1.8f);
	Ability _ability2 = new Ability("Heroic Strike", 1.2f);
	AddAbility(_ability);
	AddAbility(_ability2);
}

 

Schnell sollte hier klar werden, dass sich das so auf Dauer nicht so schön bearbeiten lässt und wir Unmengen an Variablen erzeugen.

Eine Lösung werden wir uns später anschauen.

 

Um nun die Fähigkeit auch mal zu "benutzen" müssen wir sie noch auslösen.

Auch das passiert in der Player.cs!

 

Wir erweitern das Script um eine Update Methode, lösen die Fähigkeiten per Tastatur aus und benutzen den CoolDown.

void Update()
{

if(Input.GetKeyDown(KeyCode.Alpha1))
{
	if(ABILITIES["Mortal Strike"]._coolDownTimer == 0)
	{
		Debug.Log(ABILITIES["Mortal Strike"]._damageModifier);
		// _coolDownTimer auf _coolDown setzen, damit der Skill nur einmal verfügbar ist -> "Abklingzeit"!
		ABILITIES["Mortal Strike"]._coolDownTimer = ABILITIES["Mortal Strike"]._coolDown;
	}
}
if(Input.GetKeyDown(KeyCode.Alpha2))
{
	if(ABILITIES["Heroic Strike"]._coolDownTimer == 0)
	{
		Debug.Log(ABILITIES["Heroic Strike"]._damageModifier);
		// _coolDownTimer auf _coolDown setzen, damit der Skill nur einmal verfügbar ist -> "Abklingzeit"!
		ABILITIES["Heroic Strike"]._coolDownTimer = ABILITIES["Heroic Strike"]._coolDown;
	}
}
}

 

Damit die Abklingzeit wieder zurückgesetzt werden kann (wir wollen ja die Fähigkeit nach Zeit X wieder benutzen )

müssen wir sie noch updaten.

foreach(KeyValuePair<string, Ability> _kvp in ABILITIES)
{
if(_kvp.Value_coolDownTimer > 0)
{
	_kvp.Value._coolDownTimer -= Time.deltaTime;
}	
else
{
	_kvp.Value._coolDownTimer = 0;
}
}

 

Das Ganze in die Update Methode:

void Update()
{

if(Input.GetKeyDown(KeyCode.Alpha1))
{
	if(ABILITIES["Mortal Strike"]._coolDownTimer == 0)
	{
		Debug.Log(ABILITIES["Mortal Strike"]._damageModifier);
		// _coolDownTimer auf _coolDown setzen, damit der Skill nur einmal verfügbar ist -> "Abklingzeit"!
		ABILITIES["Mortal Strike"]._coolDownTimer = ABILITIES["Mortal Strike"]._coolDown;
	}
}
if(Input.GetKeyDown(KeyCode.Alpha2))
{
	if(ABILITIES["Heroic Strike"]._coolDownTimer == 0)
	{
		Debug.Log(ABILITIES["Heroic Strike"]._damageModifier);
		// _coolDownTimer auf _coolDown setzen, damit der Skill nur einmal verfügbar ist -> "Abklingzeit"!
		ABILITIES["Heroic Strike"]._coolDownTimer = ABILITIES["Heroic Strike"]._coolDown;
	}
}


foreach(KeyValuePair<string, Ability> _kvp in ABILITIES)
{
	if(_kvp.Value_coolDownTimer > 0)
	{
		_kvp.Value_coolDownTimer += Time.deltaTime;
	}	
	else
	{
		_kvp.Value_coolDownTimer = 0;
	}
}
}

Die Player.cs sollte nun so aussehen:

 


public class Player : MonoBehaviour
{
#region VARIABLES
public Dictionary<string, Ability> ABILITIES = new Dictionary<string, Ability>();
//... etc
#endregion

#region Start
void Start()
{
	Ability _ability = new Ability("Mortal Strike", 1.8f);
	Ability _ability2 = new Ability("Heroic Strike", 1.2f);
	AddAbility(_ability);
	AddAbility(_ability2);
}

void AddAbility(Ability _ability)
{
	ABILITIES.Add(_ability._name, _ability);
}
#endregion

void Update()
{
	#region INPUT
	/// Inputs werden später überarbeitet
	if(Input.GetKeyDown(KeyCode.Alpha1))
	{
		if(ABILITIES["Mortal Strike"]._coolDownTimer == 0)
		{
			Debug.Log(ABILITIES["Mortal Strike"]._damageModifier);
			// _coolDownTimer auf _coolDown setzen, damit der Skill nur einmal verfügbar ist -> "Abklingzeit"!
			ABILITIES["Mortal Strike"]._coolDownTimer = ABILITIES["Mortal Strike"]._coolDown;
		}
	}

	if(Input.GetKeyDown(KeyCode.Alpha2))
	{
		if(ABILITIES["Heroic Strike"]._coolDownTimer == 0)
		{
			Debug.Log(ABILITIES["Heroic Strike"]._damageModifier);
			// _coolDownTimer auf _coolDown setzen, damit der Skill nur einmal verfügbar ist -> "Abklingzeit"!
			ABILITIES["Heroic Strike"]._coolDownTimer = ABILITIES["Heroic Strike"]._coolDown;
		}
	}
	 #endregion
	#region COOLDOWN
	foreach(KeyValuePair<string, Ability> _kvp in ABILITIES)
	{
		if(_kvp.Value._coolDownTimer > 0)
		{
			_kvp.Value._coolDownTimer --= Time.deltaTime;
		}	
		else
		{
			_kvp.Value._coolDownTimer = 0;
		}
	}
#endregion
}
#region SHOW COOLDOWNS

void OnGUI()
{
	foreach (KeyValuePair<string, Ability> _kvp in ABILITIES)
	{
		GUILayout.Label(_kvp.Value._name + " ready in: " + _kvp.Value._coolDownTimer.ToString());
	}
}
#endregion

}

 

 

Soweit, so gut.

Im nächsten Teil kümmern wir uns darum, dass die Abilities und ihre Werte einfacher zu modifizieren sind, ohne sie jedes Mal in der Start Methode neu zu setzen.

Danach integrieren wir ein kleines Kampfsystem in dem wir in der Console ein wenig "Schattenboxen" um so die Fähigkeiten zu testen ;)

 

 

 

#edit: bugfrei!

 

Dropbox Link zum Package - ohne jedoch die Herangehensweise zu verstehen bringt es nicht viel;)

 

Weiter geht´s hier --> rpg-basics-abilities-teil-2 <--

bearbeitet von AgentCodeMonkey
Link zu diesem Kommentar
Auf anderen Seiten teilen

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...