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

1

19.02.2022, 17:06

Erstellen von Testrüstungen mit Verstärkungsmöglichkeiten

Hallo zusammen,

ich versuche gerade für mein Lernprogramm (Consolenadventure (später WPF, Unity o.ä.) eine kleine ItemDataBase zu erstellen. Mit dabei sind natürlich Items wie Armor, Weapon u.s.w.

Derzeit habe ich mir testweise mal meine Vorstellungen vom Armorsystem vorgenommen. Ich möchte für die Charaktere eine Rüstung erstellen können, welche nach der Herstellung über diverse Grundattribute verfügen. Dabei geht es mir primär um die einzelnen DmgProtection Werte der Rüstung.
Nach der Erstellung soll jede Rüstung ein Array besitzen, welches Slots für enhancements zur verfügung stellen soll.

So weit so gut. Ich habe meine Rüstung mit 3 Werten erstellt. 1. KineticProtection 2. AcidProtection 3. MagicalProtection
Das Einfügen der enhancements in das Array bekomme ich soweit auch hin, sollte ein Platz im Array belegt sein, so läuft es zum nächsten Platz und versucht es dort. Bis die Methode auf einen freien Platz trifft und das enhencement dort einfügt. Danach springt das Programm wieder aus der Schleif und die Methode ist beendet.

Jetzt sollen die Werte der enhancments allerdings den einzelnen Werten der Rüstung hinzugefügt werden. Eine Kinetikverstärkung wird auf den Grundwert der Rüstung draufgerechnet, gleiches bei einer Säureverstärkung u.s.w.

Ich hatte es über den getter und setter der einzelnen werte versucht, aber genau da zerbreche ich mir immer den Kopf.

Einfach ausgedrückt lautet meine Idee: Schaue ob in den Slots[] eine Verstärkung des Typs Kinetik ist, wenn du eine findest dann rechne sie bitte auf den Wert KineticProtecion drauf, ansonsten nehme einfach den gegebenen Wert.

Kann mir da eventuell jemand etwas helfen, bzw. vielleicht einen Denkanstoß geben?

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AmorSlotTest
{
    public enum ArmorType { Composite, LeatherArmor}

    internal class Program
    {
        static void Main(string[] args)
        {
            Enhancement kin_enhance = new Enhancement("Kinetische Verstärkung", 50);
            Enhancement enhancement1 = new Enhancement("Magische Verstärkung", 100);

            Armor prototyp = new Armor("Testrüstung", ArmorType.Composite, 100, 50, 60);
            prototyp.GetInformation();

            Console.WriteLine();

            prototyp.EnhanceArmor(kin_enhance);

            prototyp.GetInformation();

            Console.WriteLine();

            prototyp.EnhanceArmor(enhancement1);

            prototyp.GetInformation();
            Console.ReadKey();
        }
    }

    public class Enhancement
    {
        public string Name { get; set; }
        public int EnhanceValue { get; set; }

        public Enhancement(string name, int e_value)
        {
            this.Name = name;
            this.EnhanceValue = e_value;
        }
    }

    public class Armor
    {
        public string Name { get; set; }
        public ArmorType Type { get; set; }

        public int KineticProtection { get; set; }
        public int AcidProtection { get; set; }
        public int MagicalProtection { get; set; }

        public object[] Slot {get; set;}

        public Armor(string name, ArmorType type, int kinprotection , int acidprotection, int magicalprotection)
        {
            Name = name;
            Type = type;
            KineticProtection = kinprotection;
            AcidProtection = acidprotection;
            MagicalProtection = magicalprotection;
            GetSlots();
        }

        public void EnhanceArmor(object enhancement)
        {
            for(int i = 0; i < Slot.Length; i++)
            {
                if(Slot[i] != null)
                {
                    Console.WriteLine("Slot ist voll");
                }
                else if(Slot[i] == null)
                {
                    Slot[i] = enhancement;
                    break;
                }
            }
        }

        public void GetSlots()
        {
            if(Type == ArmorType.Composite)
            {
                Slot = new object[4];
            }
            else Slot = new object[2];
        }

        public void SumSlots()
        {
            foreach(object slot in Slot)
            {
                Console.WriteLine(slot);
            }
        }

        public void GetInformation()
        {
            Console.WriteLine("Name: {0}\nArmorTyp: {1}\nKinetik: {2}\nSäure: {3}\nMagisch: {4}" , Name , Type , KineticProtection , AcidProtection , MagicalProtection);
            SumSlots();
        }
    }
}


Dankeschön

Gewinde :)

Jonathan

Community-Fossil

  • Private Nachricht senden

2

20.02.2022, 10:47

Du hast also eine Rüstung mit Basiswerten, die durch Erweiterungen verbessert werden kann? Und jede Rüstung kann bis zu 4 Erweiterungen haben, die beliebige Werte steigern?

Zunächst musst du dann zwischen Grund- und Effektivwerten unterscheiden. Ist die Grundrüstung 5 und die Erweiterung erhöht den Wert um 3, dann ist der Effektivwert 5+3=8.
Das Interface sollte dann also ungefähr so aussehen:

C-/C++-Quelltext

1
2
3
SetBaseKinetic()
AddEnhancement(enh)
GetEffectiveKinetic()

Wichtig ist, dass man Effektivwerte von außen nicht setzen darf - die ergeben sich ja immer aus den Grundwerten und den Erweiterungen.

Jetzt hast du auch die Wahl zwischen Lazy-Evaluation und Eager-Evaluation:

Eager-Evaluation:
Die Klasse speichert tatsächlich Grund- und Effektivwerte als zwei verschiedene Member. Jedesmal wenn eine Erweiterung hinzugefügt oder entfernt wird, rufst du eine "CalculateEffectiveValues()" Funktion auf, die alle Effektivwerte neu berechnet. GetEffectiveKinetic() gibt dann einfach den entsprechenden, vorberechneten Wert zurück.

Lazy-Evaluation:
Du speicherst nur die Grundwerte als Member. Werden Erweiterungen hinzugefügt passiert erstmal gar nichts. Sobald du GetEffectiveKinetic() aufrufst, werden im Getter alle Erweiterungen betrachtet und der Effektivwert berechnet.

Theoretisch macht Eager-Evaluation mehr Sinn, wenn du selten die Erweiterungen änderst, häufig die Effektivwerte abfragst, und zusätzlicher Speicherbedarf egal ist. Lazy-Evaluation macht umgekehrt Sinn wenn du häufig die Erweiterungen änderst, selten die Werte abfragst und Speicher knapp ist. In diesem Fall ist es total egal, was du nimmst, aber es ist eine nette Übung sich darüber Gedanken zu machen.
Lieber dumm fragen, als dumm bleiben!

3

20.02.2022, 12:27

Danke für deine Antwort,
also ich gehe davon aus, dass die Abfrage der Werte im Focus steht. In einem Spiel baut man in der Regel weit weniger Rüstungen als man damit Kämpft. Bevor ich deine Antwort gelesen habe, bin ich auf eine andere Lösung gekomme. Ob sich diese zwecks Rechenleistung eher als subobtimal erweist, kann ich als hobby c# Programmieranfänger nicht wirklich bewerten. In einem Solospiel sollte es sicherlich keine Probleme geben. Sollte man diese Variante allerdings in einem mmorpg verarbeiten, könnte es vielleicht zu erheblichen Spitzen im Speicher kommen. Mein bisheriges Problem bestand darin das ich versucht habe ein object in einen integer zu stecken. Dabei hat VS dann immer gemeckert. Ich werde deine Idee mal weiter verfolgen und die Variante mit den Basiswerten ( nicht als Array sondern als int) nochmal versuchen zu ermöglichen.

In meiner Lösung besteht die Rüstung nun aus mehreren Arrays, welche je nach Rüstungsart ( Leather, Padded, Composite u.s.w.) anders ausfallen sollen. Damit kann ein Handwerker diese Arrays mit mehreren Bauteilen bestücken, wovon jedes Teil ( auf Grund von Handwerksqualität) anders ausfallen könnte. Dabei kam mir allerdings noch eine weitere Idee. Es wäre schon cool, wenn man als Besitzer einer Rüstung nicht nur Rüstwerte verändern könnte, sondern als Alternative die möglichkeit hätte, zwischen Rüstwert oder Special zu wählen( z.B. dodge +3%). Hier müsste es dann allerdings eine RüstungswertArray obergrenze geben, wie z.b. Padded max. 6 Arrayplätze. Diese können dann je nach Wunsch befüllt werden. Z.B. 2 Kinetik 4 Magisch oder 3 Kinetik 2 Magisch 1 Special dodge3%.

Hier erstmal meine Idee mit den einzelnen Arrays.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ArmorSlotTest2
{
    public enum ArmorType { LeatherArmor, PaddedArmor}
    public enum EnhancementType { Kinetic, Magic}

    internal class Program
    {
        static void Main(string[] args)
        {
            Armor prototyp = new Armor(ArmorType.LeatherArmor);
            ArmorEnhancement enhancement = new ArmorEnhancement(EnhancementType.Kinetic, 30);
            ArmorEnhancement enhancement1 = new ArmorEnhancement(EnhancementType.Magic, 20);
            ArmorEnhancement enhancement2 = new ArmorEnhancement(EnhancementType.Kinetic, 40);
            ArmorEnhancement enhancement3 = new ArmorEnhancement(EnhancementType.Magic, 10);
            ArmorEnhancement enhancement4 = new ArmorEnhancement(EnhancementType.Magic, 40);

            prototyp.CalculateProtection(enhancement);
            prototyp.CalculateProtection(enhancement1);
            prototyp.GetInformation();
            Console.ReadKey();
            prototyp.CalculateProtection(enhancement3);
            prototyp.GetInformation();
            Console.ReadKey();
            prototyp.CalculateProtection(enhancement4);
            prototyp.CalculateProtection(enhancement2);
            prototyp.GetInformation();
            Console.ReadKey();
        }
    }

    internal class ArmorEnhancement
    {
        public EnhancementType E_Type { get; set; }
        public int Kinetic { get; set; }
        public int Magic { get; set; }

        public ArmorEnhancement(EnhancementType e_Type, int e_value)
        {
            E_Type = e_Type;

            if(E_Type == EnhancementType.Kinetic)
            {
                Kinetic = e_value;
            }
            else if(E_Type == EnhancementType.Magic)
            {
                Magic = e_value;
            }
        }
    }

    internal class Armor
    {
        public ArmorType A_Type { get; set; }
        public int[] Kinprotection { get; set; }
        public int[] MagicProtection { get; set; }

        public Armor(ArmorType a_type)
        {
            A_Type = a_type;

            if(A_Type == ArmorType.LeatherArmor)
            {
                Kinprotection = new int[2];
                MagicProtection = new int[3];
            }
            else if(A_Type == ArmorType.PaddedArmor)
            {
                Kinprotection = new int[4];
                MagicProtection = new int[1];
            }
        }

        public void CalculateProtection(ArmorEnhancement enhancement)
        {
            if(enhancement.E_Type == EnhancementType.Kinetic)
            {
                for(int i = 0; i < Kinprotection.Length; i++)
                {
                    if (Kinprotection[i] == 0)
                    {
                        Kinprotection[i] = enhancement.Kinetic;
                        break;
                    }
                    else continue;
                }
            }
            else if(enhancement.E_Type == EnhancementType.Magic)
            {
                for(int i = 0; i < MagicProtection.Length; i++)
                {
                    if(MagicProtection[i] != 0)
                    {
                        continue;
                    }
                    else
                    {
                        MagicProtection[i] = enhancement.Magic;
                        break;
                    }
                }
            }
        }

        private void GetShieldInformation()
        {
            int kin_protecion_sum = 0;
            int mag_protecion_sum = 0;

            for(int i = 0; i < Kinprotection.Length; i++)
            {
                kin_protecion_sum += Kinprotection[i]; 
            }

            for(int i = 0; i < MagicProtection.Length; i++)
            {
                mag_protecion_sum += MagicProtection[i];
            }

            Console.WriteLine(kin_protecion_sum);
            Console.WriteLine(mag_protecion_sum);
        }

        public void GetInformation()
        {
            Console.WriteLine("Rüstungsart: " + A_Type);
            GetShieldInformation();
        }
    }
}

Renegade

Alter Hase

Beiträge: 494

Wohnort: Berlin

Beruf: Certified Unity Developer

  • Private Nachricht senden

4

25.03.2022, 21:31

Dein Anwendungsfall ist ein schönes Beispiel für einen klassischen Dekorierer: https://de.wikipedia.org/wiki/Decorator. Ein C# Beispiel ist im Wiki enthalten. Der Dekorierer gehört zu den Strukturmustern und ermöglicht das Erweitern von Funktionalität einer Klasse. In diesem Zusammnenhang kann ich das Buch "Entwurfsmuster von Kopf bis Fuß Buch" von Elisabeth Robson, Eric Freeman und Kathy Sierra sehr empfehlen; da ich deine Frage zu Büchern in einem anderen Thread las.
Liebe Grüße,
René

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Renegade« (25.03.2022, 21:37)


5

23.07.2022, 15:59

Hallo zusammen,

danke für die Antworten und vor allem für den Tipp mit dem Dokorationspattern. Dieses habe ich zwar nicht in diesem Beispiel verwendet, aber dafür in einem anderen. Dazu hätte ich eine Frage. Ich habe das Pattern für eine Charaktererschaffung nutzen wollen, dies funktioniert soweit auch. Ich erschaffe einen normalen Charakter, dieser kann dann zum Kämpfer oder Magier spezialisiert werden.
Zum Probieren habe ich versucht meinen Charakter weiter zu spezialisieren. Zuerst wurde er ein Kämpfer, danach sollte er ein Kämpfer/Magier werden. Laut der Ausgabe der Konsole funktioniert dies auch. Für jede Spezialisierung wird eine Methode hinzu gefügt und die Attribute verändert. Normal hat der Character 10 Punkte pro Attribut, nach der ersten Spezialisierung zum Kämpfer werden diese teilweise um 5 erhöht. Sobald der Kämpfer zum Kämpfer/Magier spezialisiert wird, werden die Attribute vom Konstruktor des Magiers aber nicht mitgenommen. Diese sollten für bestimmte Attribute um weitere 5 Punkte erhöht werden. Der Charakter behält allerdings die Attribute eines Kämpfers bei, bekommt aber die zusätzliche Methode eines Magiers.

Kann mir vielleicht jemand sagen warum dies so ist?

Normale Humanklasse:

Der Dekorator:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    internal abstract class InhabitantSpecialization : IInhabitant
    {
        protected IInhabitant inhabitant;

        public InhabitantSpecialization(IInhabitant inhabitant)
        {
            this.inhabitant = inhabitant;
        }

        public abstract string FirstName { get; set; }
        public abstract string LastName { get; set; }
        public abstract Size Size { get; set; }
        public abstract Mass Mass { get; set; }
        public abstract int Strength { get; set; }
        public abstract int Stamina { get; set; }
        public abstract int Agility { get; set; }
        public abstract int Sense { get; set; }
        public abstract int Intelligent { get; set; }
        public abstract int Psychic { get; set; }

        public abstract string GetDescription();
    }


Normale Characterklasse:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    internal class Human : IInhabitant
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Size Size { get; set; }
        public Mass Mass { get; set; }

        public int Strength { get; set; }
        public int Stamina { get; set; }
        public int Agility { get; set; }
        public int Sense { get; set; }
        public int Intelligent { get; set; }
        public int Psychic { get; set; }

        public Human(string firstName, string lastName, Size size, Mass mass)
        {
            FirstName = firstName;
            LastName = lastName;
            Size = size;
            Mass = mass;
            Strength = 10;
            Stamina = 10;
            Agility = 10;
            Sense = 10;
            Intelligent = 10;
            Psychic = 10;
        }

        public string GetDescription()
        {
            return $"Vorname: {FirstName}\nNachname: {LastName}\nKörpergröße: {Size}\nKörpermasse: {Mass}\n" +
                $"Stärke: {Strength}\nAusdauer: {Stamina}\nBeweglichkeit: {Agility}\nWahrnehmung: {Sense}\n" + 
                $"Intelligenz: {Intelligent}\nPsysche: {Psychic}";
        }


Erstes upgrade auf Fighterklasse:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    internal class InhabitantStreetFighter : InhabitantSpecialization
    {
        public InhabitantStreetFighter(IInhabitant inhabitant) : base(inhabitant)
        {
            inhabitant.Strength += 5;
            inhabitant.Stamina += 5;
            inhabitant.Agility += 5;
            inhabitant.Sense += 5;
            inhabitant.Psychic += 5;
        }

        public override string FirstName { get; set; }
        public override string LastName { get; set; }
        public override Size Size { get; set; }
        public override Mass Mass { get; set; }
        public override int Strength { get; set; }
        public override int Stamina { get; set; }
        public override int Agility { get; set; }
        public override int Sense { get; set; }
        public override int Intelligent { get; set; }
        public override int Psychic { get; set; }

        public override string GetDescription()
        {
            return inhabitant.GetDescription() + "\nCharaktereigenschaft: Streetfighter";
        }

        public string SpecialMove()
        {
            return $"{inhabitant.FirstName} führt seinen Specialmove aus!!!";
        }


Zweite Aufwertung auf mageklasse:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    internal class InhabitantMage : InhabitantSpecialization
    {
        public InhabitantMage(IInhabitant inhabitant) : base(inhabitant.)
        {
            inhabitant.Sense += 5;
            inhabitant.Intelligent += 5;
            inhabitant.Psychic += 5;
        }

        public override string FirstName { get; set; }
        public override string LastName { get; set; }
        public override Size Size { get; set; }
        public override Mass Mass { get; set; }
        public override int Strength { get; set; }
        public override int Stamina { get; set; }
        public override int Agility { get; set; }
        public override int Sense { get; set; }
        public override int Intelligent { get; set; }
        public override int Psychic { get; set; }

        public override string GetDescription()
        {
            return inhabitant.GetDescription() + "\nCharaktereigenschaft: Magier ";
        }

        public string SpecialMove()
        {
            return $"{inhabitant.FirstName} führt einen Zauber aus!!!";
        }


Gruß Gewinde :)

Renegade

Alter Hase

Beiträge: 494

Wohnort: Berlin

Beruf: Certified Unity Developer

  • Private Nachricht senden

6

27.07.2022, 11:34

Du gibst bei der Description die Daten aus deinem Inhabitant aus, sowie den jeweiligen Special Move. Auch erhöhst du korrekt die Werte im Inhabitant durch die Konstruktoren.
Die Werte in den Properties sind aber nicht aus dem Inhabitant, sondern von der InhabitantSpecialization. Die Properties die du in InhabitantStreetFighter und in InhabitantMage überschreibst sind die abstrakten Properties aus InhabitantSpecialization und nicht die des Human. Das führt beim Auslesen der Daten dann nicht zu den Daten des Inhabitant die du eigentlich haben möchtest.

Du kannst das Überschreiben in den abgeleiteten Klassen der Properties eigentlich lassen und in deiner abstrakten InhabitantSpecialization direkt die Werte vom Inhabitant ausgeben:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    internal abstract class InhabitantSpecialization : IInhabitant
    {
        protected IInhabitant inhabitant;

        public InhabitantSpecialization(IInhabitant inhabitant)
        {
            this.inhabitant = inhabitant;
        }

        public string FirstName 
        {
            get => inhabitant.FirstName;
            set => inhabitant.FirstName = value;
        }
        //[...] restlichen Properties

        public abstract string GetDescription();
    }
Liebe Grüße,
René

7

29.07.2022, 21:15

Hallo Renegade,
Dankeschön für deine Antwort. Mir wurde dieser Fehler auch schon von anderer Stelle aufgezeigt. Zu meiner Erleichterung handelt es sich hierbei eher um einen Denkfehler als um fehlerhaften Code. Das beruhigt mich etwas. ^^

Dankeschön Gewinde

Werbeanzeige