CrownToFish
Neues Mitglied
Hallo zusammen,
ich möchte die Gelegenheit nutzen, mich hier kurz vorzustellen: Mein Name ist Micha, und ich habe eine Leidenschaft für die PC-Bastelei und Spieleentwicklung. Aktuell arbeitete viel mit der Unreal Engine und aktuell mit dem RPG Maker MZ.
Schon seit längerem hege ich den Traum, das Programmieren zu erlernen, doch bisher schreckten mich die vermeintlichen Schwierigkeiten und die Annahme ab, man müsse dafür studiert haben. Nun, kurz vor meinem 30. Geburtstag, habe ich den Schritt gewagt und mich mit KI-Unterstützung und vorgefertigten Skripten an umfassendere Programmierprojekte herangetraut. So ist es mir gelungen, ein komplexes Plugin für den RPG Maker MZ zu entwickeln, auch wenn mein Verständnis für die zugrundeliegenden Codes noch ausbaufähig ist bzw. fast nicht existent.
Hierbei wende ich mich an euch:
Meine Erfahrungen beschränken sich bislang auf Basiskenntnisse in verschiedenen Sprachen wie Python, JavaScript und C++. Im Zuge meines aktuellen Projekts habe ich mich für JavaScript entschieden, eine Sprache, die anscheinend leicht zu erlernen, aber schwer zu meistern ist.
Kürzlich stieß ich auf ein Beratungsangebot zur Erlernung von HTML, CSS und JavaScript in einem 9-monatigen Kurs, der auf einen Quereinstieg abzielt. Die Webseite und der YouTube-Kanal des Anbieters, Frederik Diekmann, sind hier zu finden:
Nun zu meinen Fragen rund um das Erlernen von JavaScript:
Zu guter Letzt möchte ich noch mein aktuelles Projekt vorstellen, ein Plugin für den RPG Maker, das ich mit Hilfe von ChatGPT entwickelt habe. Der Code mag noch nicht perfekt sein, aber ich bin stolz auf das Erreichte. Einen Einblick in die Funktionsweise des Plugins bietet dieses Video:
Hier das Script dazu. Steinigt mich nicht für diese Unordnung. Schwer den Code zu pflegen oder gut zu strukturieren, wenn man die Sprache nicht versteht. Dennoch habe ich richtig Spaß bei diesem Projekt.
Mein Ziel ist es, solche Projekte zukünftig eigenständig und ohne KI-Unterstützung umsetzen zu können. Ich denke die Registrierung in diesem Forum war der erste Schritt in die richtige Richtung.
ich möchte die Gelegenheit nutzen, mich hier kurz vorzustellen: Mein Name ist Micha, und ich habe eine Leidenschaft für die PC-Bastelei und Spieleentwicklung. Aktuell arbeitete viel mit der Unreal Engine und aktuell mit dem RPG Maker MZ.
Schon seit längerem hege ich den Traum, das Programmieren zu erlernen, doch bisher schreckten mich die vermeintlichen Schwierigkeiten und die Annahme ab, man müsse dafür studiert haben. Nun, kurz vor meinem 30. Geburtstag, habe ich den Schritt gewagt und mich mit KI-Unterstützung und vorgefertigten Skripten an umfassendere Programmierprojekte herangetraut. So ist es mir gelungen, ein komplexes Plugin für den RPG Maker MZ zu entwickeln, auch wenn mein Verständnis für die zugrundeliegenden Codes noch ausbaufähig ist bzw. fast nicht existent.
Hierbei wende ich mich an euch:
Meine Erfahrungen beschränken sich bislang auf Basiskenntnisse in verschiedenen Sprachen wie Python, JavaScript und C++. Im Zuge meines aktuellen Projekts habe ich mich für JavaScript entschieden, eine Sprache, die anscheinend leicht zu erlernen, aber schwer zu meistern ist.
Kürzlich stieß ich auf ein Beratungsangebot zur Erlernung von HTML, CSS und JavaScript in einem 9-monatigen Kurs, der auf einen Quereinstieg abzielt. Die Webseite und der YouTube-Kanal des Anbieters, Frederik Diekmann, sind hier zu finden:
- YouTube: Frederik Diekmann
- Webseite: DevKarriere
Nun zu meinen Fragen rund um das Erlernen von JavaScript:
- Ist JavaScript eine empfehlenswerte Sprache für Anfänger?
- Welche Herangehensweise würdet ihr empfehlen?
- Ist es ratsam, sich das Programmieren selbst beizubringen, oder besteht die Gefahr, sich schlechte Praktiken anzueignen?
- Könnt ihr Lernmaterialien wie Bücher, Websites oder Onlinekurse empfehlen?
Zu guter Letzt möchte ich noch mein aktuelles Projekt vorstellen, ein Plugin für den RPG Maker, das ich mit Hilfe von ChatGPT entwickelt habe. Der Code mag noch nicht perfekt sein, aber ich bin stolz auf das Erreichte. Einen Einblick in die Funktionsweise des Plugins bietet dieses Video:
CrownRPGAttribute | RPG Maker MZ Plugin Teaser
www.youtube.com
Hier das Script dazu. Steinigt mich nicht für diese Unordnung. Schwer den Code zu pflegen oder gut zu strukturieren, wenn man die Sprache nicht versteht. Dennoch habe ich richtig Spaß bei diesem Projekt.
CrownRPGAttribute:
/*:
* @plugindesc CrownRPGAttributes - Ein umfassendes Attributpunkt-Management-Plugin für den RPG Maker MZ.
* @author CrownToFish
*
* @help
* Das CrownRPGAttribute System ermöglicht es Spielern, Attributpunkte zu
* verwalten und zu verteilen.
* Spieler erhalten bei Spielbeginn und bei jedem Levelaufstieg
* Attributpunkte.
*
* Außerdem bietet es eine breite Palette an weiteren Funktionen die nützlich
* sind und es wird noch mehr dazu kommen.
*
* Plugin-Befehle:
* DistributePoints - Verteilt angegebene Punkte auf ein Attribut. Zum
* Beispiel können so festgelegte NPCs dem Spieler etwas beibringen sofern
* er genug verfügbare Punkte besitzt.
*
* Attribut-Struktur:
* Du kannst sämtliche Parameter selber festlegen. Darunter eine eigene
* Variable setzen um die aktuellen Punkte des jeweiligen Attributs zu
* speichern und überall abzurufen und zu nutzen. Zum Beispiel kannst du
* Dialogen so mehr Tiefe geben und die Handlung beeinflussen.
*
* ACHTUNG: Das Plugin bietet derzeit keine Funktion Spielerstats zu
* beeinflussen. Es wird aber als optionale Funktion nachgereicht.
*
* @param Attributes
* @type struct<Attribute>[]
* @desc Passe die Attribute an.
*
* @param StartPoints
* @text Startpunkte
* @type number
* @desc Die verfügbaren Attributpunkte bei einem neuen Spielstart.
* @default 0
*
* @param MaxPoints
* @text Maximale Punkte
* @type number
* @desc Die maximale Anzahl an Punkten, die ein Spieler im Spielverlauf erhalten kann.
* @default 50
*
* @param PointsPerLevel
* @text Punkte pro Levelaufstieg
* @type number
* @desc Die Anzahl der Punkte, die ein Spieler pro Levelaufstieg erhält.
* @default 1
*
* @command DistributePoints
* @text Punkte verteilen
* @desc Erstelle einen PC und lass ihn dem Spielcharakter etwas beibringen.
*
* @arg AttributeName
* @text Attributname
* @desc Der Name des Attributs, auf das die Punkte verteilt werden sollen.
* @type text
*
* @arg Points
* @text Punkte
* @desc Anzahl der Punkte, die auf das Attribut verteilt werden sollen.
* @type number
* @min 1
*
* @param SuccessSound
* @text Erfolgs-Sound
* @type file
* @dir audio/se/
* @desc Der Sound, der abgespielt wird, wenn ein Punkt erfolgreich verteilt wird.
*
* @param FailureSound
* @text Fehlschlag-Sound
* @type file
* @dir audio/se/
* @desc Der Sound, der abgespielt wird, wenn ein Punkt nicht verteilt werden kann.
*
* @param SuccessMessage
* @text Erfolgsmeldung
* @desc Die Meldung, die angezeigt wird, wenn Punkte erfolgreich verteilt werden. Verwende {attribute}, {points} und {totalPoints} um weitere Informationen im Text anzuzeigen.
* @type text
* @default {attribute} wurde um {points} erhöht. Du hast jetzt {totalPoints} Punkte.
*
* @param FailureMessage
* @text Fehlermeldung
* @desc Die Meldung, die angezeigt wird, wenn die Verteilung eines Punktes fehlschlägt.
* @default Punktverteilung auf {attribute} fehlgeschlagen!
*
* * @param NPCs
* @type struct<NPC>[]
* @desc Definiere die NPCs, die im Spiel verwendet werden.
* @default []
*
* @command DiscoverNPC
* @text NPC entdecken
* @desc Markiert einen NPC als entdeckt.
*
* @arg NPCName
* @text NPC Name
* @desc Der Name des NPCs, der als entdeckt markiert werden soll.
* @type text
*
*/
/*~struct~NPC:
* @param Name
* @desc Der Name des NPCs.
* @type text
*
* @param Ort
* @desc Der Ort, an dem der NPC gefunden werden kann.
* @type text
*
* @param Attribut
* @desc Das Attribut, das durch den NPC beeinflusst wird.
* @type text
*
* @param Beschreibung
* @desc Eine kurze Beschreibung des NPCs.
* @type note
*
* @param BildEntdeckt
* @desc Das Bild des NPCs, wenn er entdeckt wurde.
* @type file
* @dir img/pictures/
*
* @param BildVerdeckt
* @desc Das Bild des NPCs, wenn er noch nicht entdeckt wurde.
* @type file
* @dir img/pictures/
*
* @param BildNormal
* @desc Das Hintergrundbild für den normalen Zustand des Buttons.
* @type file
* @dir img/pictures/
*
* @param BildHovered
* @desc Das Hintergrundbild für den hervorgehobenen Zustand des Buttons.
* @type file
* @dir img/pictures/
*
*/
/*~struct~Attribute:
* @param Name
* @desc Der Name des Attributs.
* @type string
*
* @param Description
* @desc Die Beschreibung des Attributs.
* @type string
*
* @param MaxPoints
* @desc Die maximal verteilbaren Punkte für dieses Attribut.
* @type number
*
* @param VariableId
* @desc Die ID der RPG Maker Variable, in der der aktuelle Wert des Attributs gespeichert wird.
* @type variable
*/
(function () {
const pluginName = 'CrownRPGAttributes';
const parameters = PluginManager.parameters(pluginName);
const attributes = JSON.parse(parameters['Attributes']).map((attr) => JSON.parse(attr));
const startPoints = Number(parameters['StartPoints']);
const maxPoints = Number(parameters['MaxPoints']);
const pointsPerLevel = Number(parameters['PointsPerLevel']);
let discoveredNPCs = [];
let attributePoints = startPoints;
let attributeValues = attributes.reduce((obj, attr) => {
obj[attr.Name] = 0;
return obj;
}, {});
const _Window_MenuCommand_addMainCommands = Window_MenuCommand.prototype.addMainCommands;
Window_MenuCommand.prototype.addMainCommands = function () {
_Window_MenuCommand_addMainCommands.call(this);
this.addCommand('Attribute', 'attribute', true);
};
const _Scene_Menu_create = Scene_Menu.prototype.create;
Scene_Menu.prototype.create = function () {
_Scene_Menu_create.call(this);
this._commandWindow.setHandler('attribute', this.commandAttribute.bind(this));
};
Scene_Menu.prototype.commandAttribute = function () {
SceneManager.push(Scene_Attribute);
};
Window_NPCList.prototype.processOk = function () {
Window_Command.prototype.processOk.call(this);
if (this.currentSymbol() === 'npc') {
var npctextbox = new Window_NPCTextBox();
this.parent.addChild(npctextbox); // Fügen Sie das Fenster zum Elternobjekt hinzu, nicht direkt zu `Window_NPCList`
}
};
function Window_NPCTextBox() {
this.initialize.apply(this, arguments);
}
Window_NPCTextBox.prototype = Object.create(Window_Base.prototype);
Window_NPCTextBox.prototype.constructor = Window_NPCTextBox;
Window_NPCTextBox.prototype.initialize = function () {
var x = (Graphics.boxWidth - 600) / 2;
var y = (Graphics.boxHeight - 600) / 2;
var rect = new Rectangle(x, y, 600, 600);
Window_Base.prototype.initialize.call(this, rect);
this.refresh();
};
Window_NPCTextBox.prototype.refresh = function () {
this.contents.clear();
// Hier können Sie Text oder andere Elemente hinzufügen
this.drawText('NPC Informationen', 0, 0, this.contents.width, 'center');
};
function Scene_Attribute() {
this.initialize.apply(this, arguments);
}
Scene_Attribute.prototype = Object.create(Scene_MenuBase.prototype);
Scene_Attribute.prototype.constructor = Scene_Attribute;
Scene_Attribute.prototype.initialize = function () {
Scene_MenuBase.prototype.initialize.call(this);
};
Scene_Attribute.prototype.create = function () {
Scene_MenuBase.prototype.create.call(this);
const windowWidth = Graphics.boxWidth;
// Prozentsätze für jedes Fenster
const attributeWindowWidthPercent = 0.3;
const npcListWindowWidthPercent = 0.4;
// Berechnen der Breiten basierend auf den Prozentsätzen
const attributeWindowWidth = windowWidth * attributeWindowWidthPercent;
const npcListWindowWidth = windowWidth * npcListWindowWidthPercent;
// Erstellen der Fenster mit den berechneten Breiten
const rectAttribute = new Rectangle(0, 0, attributeWindowWidth, this.mainAreaHeight());
const rectNpcList = new Rectangle(attributeWindowWidth, 0, npcListWindowWidth, this.mainAreaHeight());
this._attributeWindow = new Window_Attribute(rectAttribute);
this._npcListWindow = new Window_NPCList(rectNpcList);
// Erstellen des Command Windows
const rectCommand = new Rectangle(attributeWindowWidth + npcListWindowWidth, 0, this.mainAreaHeight());
this.addWindow(this._attributeWindow);
this.addWindow(this._npcListWindow);
};
function Window_Attribute() {
this.initialize.apply(this, arguments);
}
Window_Attribute.prototype = Object.create(Window_Base.prototype);
Window_Attribute.prototype.constructor = Window_Attribute;
Window_Attribute.prototype.initialize = function (rect) {
Window_Base.prototype.initialize.call(this, rect);
this.refresh();
};
Scene_Attribute.prototype.start = function () {
Scene_MenuBase.prototype.start.call(this);
this._attributeWindow.refresh();
};
Window_Attribute.prototype.refresh = function () {
this.contents.clear();
this.drawHeaderText();
this.drawAttributes();
this.drawAvailablePoints();
};
Window_Attribute.prototype.drawHeaderText = function () {
this.drawText('Attribute', 0, 0, this.contents.width, 'center');
};
Window_Attribute.prototype.drawAttributes = function () {
let y = this.lineHeight();
const columnWidth = this.contents.width / 2;
const lineHeight = this.lineHeight();
const lineColor = '#808080'; // Grau für die Linienfarbe
attributes.forEach((attr, index) => {
// Zeichnet eine Linie über dem Attributnamen
this.contents.fillRect(0, y - 2, this.contents.width, 2, lineColor);
const attributeName = attr.Name;
const attributeValue = attributeValues[attributeName];
const attributeLine = `${attributeName}`;
const pointsLine = `${attributeValue} / ${attr.MaxPoints}`;
// Attributname linksbündig zeichnen
this.drawText(attributeLine, 0, y, columnWidth - 2, 'left');
// Zeichnet eine Trennlinie zwischen Attributname und Punkten
this.contents.fillRect(columnWidth - 2, y, 2, lineHeight, lineColor);
// Punkte zentriert zeichnen
this.drawText(pointsLine, columnWidth, y, columnWidth, 'center');
y += lineHeight;
// Zeichnet eine Linie über der Beschreibung
this.contents.fillRect(0, y - 2, this.contents.width, 2, lineColor);
// Beschreibung direkt unter der Linie zeichnen
this.contents.fontSize -= 2;
this.drawText(attr.Description, 0, y, this.contents.width, 'left');
this.contents.fontSize += 2;
y += lineHeight;
// Zeichnet eine Linie unter der Beschreibung
this.contents.fillRect(0, y - 2, this.contents.width, 2, lineColor);
// Abstand nach der Linie hinzufügen
y += lineHeight * 0.5;
});
};
function Window_NPCList() {
this.initialize.apply(this, arguments);
}
Window_NPCList.prototype = Object.create(Window_Command.prototype);
Window_NPCList.prototype.constructor = Window_NPCList;
Window_NPCList.prototype.initialize = function (rect) {
Window_Command.prototype.initialize.call(this, rect);
this.refresh();
};
// Laden der NPC-Daten aus den Pluginparametern
Window_NPCList.prototype.loadNPCs = function () {
const npcData = JSON.parse(PluginManager.parameters('CrownRPGAttributes')['NPCs']);
return npcData.map((npc) => JSON.parse(npc));
};
// Erstellung der NPC-Befehlsliste
Window_NPCList.prototype.makeCommandList = function () {
const npcs = this.loadNPCs();
npcs.forEach((npc) => {
this.addCommand(npc.Name, 'npc', true, npc);
});
};
PluginManager.registerCommand(pluginName, 'DiscoverNPC', (args) => {
const { NPCName } = args;
if (!discoveredNPCs.includes(NPCName)) {
discoveredNPCs.push(NPCName);
}
});
// Zeichnen jedes Elements in der Liste
Window_NPCList.prototype.drawItem = function (index) {
const command = this._list[index];
const npcName = command.name;
const lineColor = '#808080'; // Definieren Sie die Farbe für den Trennstrich
const rect = this.itemRect(index);
const isDiscovered = discoveredNPCs.includes(npcName);
//Anzeige der unentdeckten NPCs
const bitmap = isDiscovered
? ImageManager.loadPicture(command.ext.BildEntdeckt)
: ImageManager.loadPicture(command.ext.BildVerdeckt);
const displayName = isDiscovered ? npcName : '????';
const displayLocation = isDiscovered ? command.ext.Ort : 'Geheim';
const displayAttribute = isDiscovered ? command.ext.Attribut : 'Unbekannt';
const bottomPadding = 6; // Abstand zum unteren Rand des Buttons
// Zeichnen des Bildes
if (bitmap && bitmap.isReady()) {
const imageWidth = 144;
const imageHeight = 144;
const imageX = rect.x;
const imageY = rect.y + rect.height - imageHeight - bottomPadding;
this.contents.blt(bitmap, 0, 0, bitmap.width, bitmap.height, imageX, imageY, imageWidth, imageHeight);
} else if (bitmap) {
bitmap.addLoadListener(() => {
this.refresh();
});
}
// Zeichnen des Textes
const textX = rect.x + 144 + 8;
const textWidth = rect.width - 144 - 16;
const textY = rect.y + rect.height - this.lineHeight() * 3 - bottomPadding;
const columnWidth = textWidth / 2;
this.drawText(displayName, textX, textY, columnWidth, 'center');
this.drawText(`Ort: ${displayLocation}`, textX + columnWidth, textY, columnWidth, 'center');
const lineY = textY + this.lineHeight() + 2;
this.contents.fillRect(textX, lineY, textWidth, 2, lineColor); // Trennstrich
const attributeY = lineY + 4;
this.drawText(`Rolle: ${displayAttribute}`, textX, attributeY, textWidth, 'left');
};
Window_NPCList.prototype.itemHeight = function () {
return 150; // Setzen Sie hier die gewünschte Höhe für jeden Button
};
Window_NPCList.prototype.refresh = function () {
Window_Command.prototype.refresh.call(this);
// Hier können Sie zusätzliche Zeichnungsfunktionen hinzufügen, wie z.B. das Zeichnen von Bildern neben den NPC-Namen.
};
Scene_Attribute.prototype.update = function () {
Scene_MenuBase.prototype.update.call(this);
if (this.isActive() && this.isCancelTriggered()) {
this.popScene();
}
};
Scene_Attribute.prototype.isCancelTriggered = function () {
return Input.isTriggered('cancel') || TouchInput.isCancelled();
};
Window_Attribute.prototype.drawAvailablePoints = function () {
// Berechnet die Y-Position am unteren Rand des Fensters mit etwas Abstand
const y = this.contents.height - this.lineHeight() - 10; // 10 Pixel Abstand vom unteren Rand
this.drawText(`Verfügbare Punkte: ${attributePoints}`, 0, y, this.contents.width, 'left');
};
PluginManager.registerCommand(pluginName, 'AddPoint', (args) => {
const { AttributeName } = args;
if (
attributePoints > 0 &&
attributeValues[AttributeName] < attributes.find((attr) => attr.Name === AttributeName).MaxPoints
) {
attributeValues[AttributeName]++;
attributePoints--;
SceneManager._scene._attributeWindow.refresh();
}
});
PluginManager.registerCommand(pluginName, 'RemovePoint', (args) => {
const { AttributeName } = args;
if (attributeValues[AttributeName] > 0) {
attributeValues[AttributeName]--;
attributePoints++;
SceneManager._scene._attributeWindow.refresh();
}
});
const _Game_Actor_levelUp = Game_Actor.prototype.levelUp;
Game_Actor.prototype.levelUp = function () {
_Game_Actor_levelUp.call(this);
this.gainAttributePoints(pointsPerLevel);
};
PluginManager.registerCommand(pluginName, 'DistributePoints', (args) => {
const { AttributeName, Points } = args;
const pointsToDistribute = parseInt(Points);
const actor = $gameParty.leader(); // Annahme, dass der Anführer der Gruppe die Punkte erhält
// Finden des Attributs basierend auf dem übergebenen Namen
const attribute = attributes.find((attr) => attr.Name === AttributeName);
if (
attributePoints >= pointsToDistribute &&
attributeValues[AttributeName] + pointsToDistribute <= actor.maxAttributePoints(AttributeName)
) {
attributeValues[AttributeName] += pointsToDistribute;
attributePoints -= pointsToDistribute;
// Hier müssen Sie sicherstellen, dass 'attribute' existiert und eine gültige 'VariableId' hat
if (attribute && attribute.VariableId !== undefined) {
$gameVariables.setValue(attribute.VariableId, attributeValues[AttributeName]);
}
// Ersetze Platzhalter {attribute} und {points} in der Erfolgsmeldung
let successMessage = parameters['SuccessMessage'];
successMessage = successMessage.replace('{attribute}', AttributeName);
successMessage = successMessage.replace('{points}', pointsToDistribute);
successMessage = successMessage.replace('{totalPoints}', attributeValues[AttributeName]);
$gameMessage.add(successMessage);
AudioManager.playSe({ name: parameters['SuccessSound'], pan: 0, pitch: 100, volume: 90 });
} else {
AudioManager.playSe({ name: parameters['FailureSound'], pan: 0, pitch: 100, volume: 90 });
$gameMessage.add(parameters['FailureMessage']);
}
});
// Ergänzt die Game_Actor Klasse um eine Methode, die die maximalen Punkte für ein Attribut zurückgibt
Game_Actor.prototype.maxAttributePoints = function (attributeName) {
return attributes.find((attr) => attr.Name === attributeName).MaxPoints;
};
Game_Actor.prototype.gainAttributePoints = function (points) {
attributePoints += points;
if (attributePoints > maxPoints) {
attributePoints = maxPoints; // Stellt sicher, dass die maximale Punktzahl nicht überschritten wird
}
// Aktualisieren des Attribut-Fensters, wenn es offen ist
if (SceneManager._scene instanceof Scene_Attribute) {
SceneManager._scene._attributeWindow.refresh();
}
};
})();
Mein Ziel ist es, solche Projekte zukünftig eigenständig und ohne KI-Unterstützung umsetzen zu können. Ich denke die Registrierung in diesem Forum war der erste Schritt in die richtige Richtung.