Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

1

27.02.2017, 10:24

Falscher Wert noch im Zugriff

Hey,
wenn ich "Gold" aufhebe, wird der Wert laut Debugger den Lebenspunkten gutgeschrieben. Klingt falsch, ist es auch.

Beim Aufruf der Methode, hat er noch nicht den Datensatz "CurrentGold" im Zugriff sondern noch den Datensatz "CurrentLife".

Ich hoffe, ich habe "nur" einen Fehler in meiner Datenstruktur, diese sieht wie folgt aus:

C#-Quelltext

1
2
3
4
5
public abstract class ChangeData : PlayerCommonData
{
    public float CurrentValue { get; set; }
    public float MaxValue { get; set; }
}


Da meine Daten alle einen Maximalwert haben, kann ich dann von dieser Klasse erben und setze die Werte

Die Lebenspunkte

C#-Quelltext

1
2
3
4
5
6
7
8
public class PlayerHealthData : ChangeData
{
    private void Awake()
    {
        MaxValue = 100;
        CurrentValue = MaxValue;
    }
}


Das Gold

C#-Quelltext

1
2
3
4
5
6
7
8
public class PlayerGoldData : ChangeData
{
    private void Awake()
    {
        CurrentValue = 0;
        MaxValue = float.MaxValue;
    }
}


Nun zum Ablauf, der Gegner stirbt, er droppt ein Item. Bei Kollision wird das Item zerstört und der "Inhalt", also das Gold dem Spieler hinzugefügt.

Der hinzuzufügende Wert wird also vom Gegner über das Item hin zum Spieler durchgereicht, der Aufruf

C#-Quelltext

1
2
3
4
5
public void ChangeGold(float goldToAdd)
    {
        ChangeValue(goldToAdd);
        //view.UpdateBar(view.TextGold, data.CurrentValue.ToString());
    }


Zum Schluss wird also diese Methode aufgerufen:

C#-Quelltext

1
2
3
4
protected void ChangeValue(float valueToAdd)
    {
        commonData.CurrentValue = Mathf.Clamp(commonData.CurrentValue + valueToAdd, 0, commonData.MaxValue);
    }


Laut Debugger ist der "valueToAdd" so, wie er sein soll. CurrentValue steht aber noch auf Lebenspunkte anstatt sich dem benötigten Wert anzupassen.

Bekommt die Methode den Wechsel noch nicht mit? Oder kann ich CurrentValue in den Startmethoden nicht einfach so mehrfach setzen..?

Weil das Ganze schwer zu beschreiben ist und zu viele Klassen behandelt werden, hier mal das Repo auf Github https://github.com/Garzec/DarknessFalls

Das Ganze spielt sich, sobald das Item die Methode "GoldOnCollision()" aufruft, um Gold hinzuzufügen (https://github.com/Garzec/DarknessFalls/…ldController.cs) nur noch im Spielerordner ab (https://github.com/Garzec/DarknessFalls/…/Scripts/Player). All diese Scripte liegen auf dem SpielerObjekt.

Innerhalb des Datenordners befindet sich die Basis "ChangeValues", drunter sind dann die einzelnen Klassen, wie Gold, Leben, etc. wo der CurrentValue gesetzt wird. In dem Controller Ordner befindet sich in der Basisklasse "PlayerCommonController" dann die Methode "ChangeValue(float valueToAdd)", aufgerufen dann wieder von den erbenden Klassen eine Ebene tiefer.

Danke für Antworten :)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Garzec« (27.02.2017, 10:58)


Wirago

Alter Hase

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

2

27.02.2017, 10:45

Offenbar ist das Objekt "commonData" vom Typ "PlayerHealthData". ist auch im Debugger zu sehen welches Objekt dahinter steht. Wo kommt denn dein commonData-Obj her?

Kilo

Frischling

Beiträge: 74

Wohnort: Aachen

  • Private Nachricht senden

3

27.02.2017, 10:59

Also ich würde mal versuchen CurrentValueGold und CurrentValueHealth zu benennen.
Das wäre jedenfalls der erste Test den ich als Laie machen würde :)

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

4

27.02.2017, 11:06

@Wirago, ich habe meine Infos mal überarbeitet, ich hoffe im Github Repo wird es übersichtlicher.

commonData ist eine Instanz von "ChangeValues", also der Basis der Datenklassen. Und ja, deine Idee ist richtig, commonData verweist auf die Lebenspunkte. In der Console zeigt er für commonData an:

Zitat

PlayerObject (PlayerHealthData)
UnityEngine.Debug:Log(Object)
PlayerCommonController:ChangeValue(Single) (at Assets/Scripts/Player/Controller/PlayerCommonController.cs:16)
PlayerGoldController:ChangeGold(Single) (at Assets/Scripts/Player/Controller/ChangeValues/Gold/PlayerGoldController.cs:18)
ItemGoldController:GoldOnCollision() (at Assets/Scripts/Collectables/Items/Controller/Item/Gold/ItemGoldController.cs:16)
ItemGoldController:OnCollisionEnter(Collision) (at Assets/Scripts/Collectables/Items/Controller/Item/Gold/ItemGoldController.cs:30)


Aber wie kann ich sagen, wechsel das Objekt, du bist jetzt Gold, nicht mehr Leben?

Dürfte ich also nicht oben im Script eine Instanz der Basis bilden, sondern muss die jeweilige Datenklasse als Parameter in der Methode mitgeben? Geht sowas überhaupt?

Wirago

Alter Hase

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

5

27.02.2017, 11:27

Hab mich kurz durch das Repo geklickt, konnte aber nirgends die Deklarierung von commonData finden. Kenne mich mit Unity nicht aus, ist das vllt so ein Unity-feature?
Wenn du eine Instanz der Basisklasse erstellst, hilft dir das nicht weiter. Wenn du Gold aufsammelst, solltest du auch ein Goldobjekt erstellen, bzw. eine passende abgeleitete Klasse der Basisklasse.

In meinem Projekt zB. habe ich eine Basisklasse "Item". Davon leiten sich dann Klassen wie "Gear" oder "Food" ab (die dann wiederum Interfaces wie Consumable oder Equipable implementieren). Wie auch immer, taucht dann ein Item auf (Kisteninhalt, Drop, etc) dann wird direkt ein Gear- oder Food-Objekt erstellt, und nicht ein "Item".


Deine Idee aus dem Healthobjekt ein Goldobjekt zu machen ist aus meiner Sicht nicht der richtige Weg und wird später nur zu Problemen führen

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

6

27.02.2017, 11:38

Also eine Instanz, wie in C# mit

C#-Quelltext

1
Klasse instanz = new Klasse();
ist in Unity mit

C#-Quelltext

1
Klasse instanz = GetComponent<Klasse>();
erledigt.

Aber wie auch schon auf dem Discord Server gesagt, vielleicht habe ich auch einfach keine Ahnung :D

Thandor

Frischling

Beiträge: 84

Wohnort: Berlin

Beruf: Softwareentwickler

  • Private Nachricht senden

7

27.02.2017, 12:20

Versuche mal dir ein Klassendiagramm zu erstellen, dann sieht man besser den Zusammenhang.
Dazu kannst du zum Beispiel Visual Paradigm verwenden.

Vergiss nicht, das Klassendiagramm hier ins Forum zu stellen am besten als Bilddatei (bmp, jpg, png, ...)
Ich mag pewn.de.

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

8

27.02.2017, 14:19

Also ich habe es jetzt gelöst bekommen, das Problem lag daran, dass in der Basisklasse nie die passende Datenklasse geändert wurde. Er blieb immer bei Health. Das Ganze sieht jetzt bei mir für das Beispiel Gold wie folgt aus:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class PlayerCommonController : MonoBehaviour
{
    protected ChangeData CommonData { get; private set; }

    protected virtual void Awake()
    {
        CommonData = CreateData();
    }

    protected abstract ChangeData CreateData();

    protected void ChangeValue(float valueToAdd)
    {
        CommonData.CurrentValue = Mathf.Clamp(CommonData.CurrentValue + valueToAdd, 0, CommonData.MaxValue);
    }
}


C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PlayerGoldController : PlayerCommonController
{
    protected PlayerGoldView view;

    protected override ChangeData CreateData()
    {
        return GetComponent<PlayerGoldData>();
    }

    private void Start()
    {
        view = GetComponent<PlayerGoldView>();
    }

    public void ChangeGold(float goldToAdd)
    {
        ChangeValue(goldToAdd);
        view.UpdateBar(view.TextGold, CommonData.CurrentValue.ToString());
    }
}


Dadurch wird durch das "CreateData()" immer die zugehörige Datenklasse gesetzt.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

9

27.02.2017, 17:41

Dass "Create" allerdings ein "Get" macht, finde ich semantisch doch sehr unschön und verwirrend.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Renegade

Alter Hase

Beiträge: 494

Wohnort: Berlin

Beruf: Certified Unity Developer

  • Private Nachricht senden

10

27.02.2017, 21:10

Dass "Create" allerdings ein "Get" macht, finde ich semantisch doch sehr unschön und verwirrend.


Nicht nur das, denn in Unity ist ein GetComponent NICHT das gleiche wie ein new, sondern eine generische Suchfunktion nach einer ableitenden Klasse vom Typ Component im aktuellen GameObject. Dabei wird überhaupt nicht gewährleistet ob diese Component auf dem GameObject existiert und kann dabei natürlich eine NullReference feuern.
Wieso wird überhaupt bei einer Datenklassen von MonoBehaviour geerbt? Sie müssen doch überhaupt kein Behaviour besitzen, geschweige denn auf einem GameObject existieren. Ich empfehle entweder eine herrkömmliche Klasse die ggf. eine Schnittstelle realisiert oder wenn du Serialisierung benötigst und ggf. die Daten im Projektverzeichnis haben willst eine Ableitung von ScriptableObject. Viele Coder gehen imo den falschen Weg und denken in Unity müsse alles von MonoBehaviour ableiten. Im diesen Sinne auch: Aggregation > Inheritance! (btw. wäre das equivalent zu Create dann wohl AddComponent<>() )

PS: Achte darauf, dass die Magic Method Awake von Unity nicht von der Elternklasse aufgerufen wird, sondern stets nur vom letzten Leaf (Unity berücksichtigt hier standardmäßig keine Vererbung). Wenn du unbedingt ableiten möchtest, muss Awake in der Elternklasse entweder virtual oder abstract sein und diese muss mit base.Awake aufgerufen werden. Das selbe gilt ebenso für alle andern Magic Methods von Unity.
Liebe Grüße,
René

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Renegade« (27.02.2017, 21:20)


Werbeanzeige