Olá, pessoal.
Estou disponibilizando um plugin simples para RPG Maker MZ que cria uma tela de seleção de personagem por cards.
A proposta é permitir que o jogador escolha entre personagens já configurados. Cada personagem pode ter sua própria imagem, nome, ator e classe.
js/plugins/
Ative no Gerenciador de Plugins.
Depois, em um evento, use o comando de script:
SistemaSelecao.iniciar();
Configuração recomendada do evento:
MoyCharacterSelector.start();
this.terminate();
Depois crie uma segunda página no mesmo evento usando o switch configurado no plugin. Assim a seleção não abre novamente.
img/pictures/
No código, use o nome da imagem sem .png.
Exemplo:
activeImage: "Actor1_1",
inactiveImage: "Actor1_1",
Para o fundo:
image: "BG",
Exemplo:
{
id: 1,
name: "Espadachim",
actorName: "Reid",
activeImage: "Actor1_1",
inactiveImage: "Actor1_1",
actorId: 1,
classId: 1
}
Campos principais:
Pode usar em projetos comerciais e não comerciais.
Pode editar conforme a necessidade do projeto.
Se redistribuir, mantenha os créditos.
Estou disponibilizando um plugin simples para RPG Maker MZ que cria uma tela de seleção de personagem por cards.
A proposta é permitir que o jogador escolha entre personagens já configurados. Cada personagem pode ter sua própria imagem, nome, ator e classe.
Recursos
- Seleção de personagem por cards.
- Suporte a imagem de fundo.
- Imagem individual para cada personagem.
- Personagem selecionado em destaque.
- Personagens não selecionados em preto e branco.
- Aplica automaticamente o ator escolhido.
- Aplica automaticamente a classe configurada.
- Pode teleportar o jogador após a escolha.
- Usa switch para impedir que o evento abra novamente.
- Funciona diretamente sobre o mapa.
Como usar
Coloque o plugin na pasta:js/plugins/
Ative no Gerenciador de Plugins.
Depois, em um evento, use o comando de script:
SistemaSelecao.iniciar();
Configuração recomendada do evento:
MoyCharacterSelector.start();
this.terminate();
Depois crie uma segunda página no mesmo evento usando o switch configurado no plugin. Assim a seleção não abre novamente.
Imagens
As imagens devem ficar em:img/pictures/
No código, use o nome da imagem sem .png.
Exemplo:
activeImage: "Actor1_1",
inactiveImage: "Actor1_1",
Para o fundo:
image: "BG",
Configuração dos personagens
Cada personagem é configurado no bloco characters.Exemplo:
{
id: 1,
name: "Espadachim",
actorName: "Reid",
activeImage: "Actor1_1",
inactiveImage: "Actor1_1",
actorId: 1,
classId: 1
}
Campos principais:
- actorId: ID do ator no banco de dados.
- classId: ID da classe aplicada ao personagem.
- actorName: nome exibido após a escolha.
- activeImage: imagem do card selecionado.
- inactiveImage: imagem do card não selecionado.
Código
Javascript:
/*:
* @target MZ
* @plugindesc v1.8.1 - Character selection scene with background, card layout, class assignment, and grayscale inactive states.
* @author Moycanow
*
* @help
* ============================================================================
* Moycanow Character Selector
* ============================================================================
*
* This plugin provides a lightweight character selection interface for
* RPG Maker MZ. It allows the player to choose between predefined characters,
* each one linked to a specific Actor ID and Class ID.
*
* The selection screen is rendered directly over the map scene and supports:
*
* - Custom background image
* - Automatic background scaling
* - Character cards
* - Card image fitting and masking
* - Selected card scaling
* - Inactive card opacity
* - True grayscale filter for inactive characters
* - Actor name override
* - Class assignment on confirmation
* - Optional party cleanup before adding the selected actor
* - Optional player transparency during selection
* - Optional map transfer after selection
* - Switch-based selection lock to prevent rerunning the event
*
* ============================================================================
* Usage
* ============================================================================
*
* Use a Script command in an event:
*
* SistemaSelecao.iniciar();
*
* or:
*
* MoyCharacterSelector.start();
*
* Recommended event setup:
*
* Page 1:
* - Trigger: Autorun or Parallel
* - Script:
*
* SistemaSelecao.iniciar();
* this.erase();
*
* Page 2:
* - Condition: Switch defined in CONFIG.doneSwitchId is ON
* - No commands required
*
* ============================================================================
* Image Setup
* ============================================================================
*
* Character card images must be placed in:
*
* img/pictures/
*
* Example:
*
* img/pictures/Actor1_1.png
*
* In the plugin configuration, reference the file without extension:
*
* activeImage: "Actor1_1"
* inactiveImage: "Actor1_1"
*
* Background image also uses img/pictures/ and must be referenced without
* extension:
*
* background.image: "BG"
*
* ============================================================================
* Configuration Notes
* ============================================================================
*
* - actorId controls which Actor from the database is added to the party.
* - classId controls which Class is assigned to that Actor.
* - actorName overrides the Actor's displayed name after selection.
* - activeImage is used while the card is selected.
* - inactiveImage is used while the card is not selected.
* - If inactiveImage is empty, activeImage can still be reused by assigning
* the same filename to both fields.
*
* ============================================================================
* Terms of Use
* ============================================================================
*
* Free to use in commercial and non-commercial RPG Maker MZ projects.
* Credit "Moycanow" when used or redistributed.
*
* Redistribution is allowed as long as this header remains intact.
* Modification is allowed for personal or project-specific needs.
*
* ============================================================================
*/
var MoyCharacterSelector = MoyCharacterSelector || {};
var SistemaSelecao = SistemaSelecao || {};
(function() {
"use strict";
const CONFIG = {
selectionVariableId: 1,
doneSwitchId: 10,
clearPartyBeforeAdd: true,
keepLevelOnClassChange: false,
transparentPlayerDuringSelection: true,
background: {
enabled: true,
image: "BG",
opacity: 255,
fallbackColor: "rgba(0, 0, 0, 0.80)",
autoFitScreen: true,
offsetX: 120,
offsetY: 0,
extraScale: 1.20
},
transfer: {
enabled: true,
mapId: 1,
x: 1,
y: 12,
direction: 2,
fadeType: 0
},
layout: {
startY: 150,
boxWidth: 165,
boxHeight: 300,
spacingX: 14,
imagePadding: 0,
imageZoom: 1.05,
fitMode: "cover",
inactiveOpacity: 170,
selectedBrightness: 0.50,
selectedContrast: 0.75,
inactiveBrightness: 0.78,
inactiveContrast: 1.08,
inactiveBlackAndWhite: true,
selectedCardScale: 1.04,
inactiveCardScale: 1.00,
nameFontSize: 20,
infoFontSize: 14,
textPanelHeight: 72
},
characters: [
{
id: 1,
name: "Espadachim",
actorName: "Reid",
activeImage: "Actor1_1",
inactiveImage: "Actor1_1",
actorId: 1,
classId: 1
},
{
id: 2,
name: "Artista Marcial",
actorName: "Gale",
activeImage: "Actor1_3",
inactiveImage: "Actor1_3",
actorId: 3,
classId: 5
},
{
id: 3,
name: "Mago",
actorName: "Albert",
activeImage: "Actor1_5",
inactiveImage: "Actor1_5",
actorId: 5,
classId: 2
},
{
id: 4,
name: "Sacerdote",
actorName: "Eliot",
activeImage: "Actor1_7",
inactiveImage: "Actor1_7",
actorId: 7,
classId: 3
}
]
};
let _active = false;
let _lastSelection = 0;
let _rootLayer = null;
let _backgroundSprite = null;
let _characterContainers = [];
function hasImageName(name) {
return name && String(name).trim().length > 0;
}
function maxSelection() {
return CONFIG.characters.length;
}
function currentSelection() {
let value = Number($gameVariables.value(CONFIG.selectionVariableId));
if (value < 1 || value > maxSelection()) {
value = 1;
$gameVariables.setValue(CONFIG.selectionVariableId, value);
}
return value;
}
function characterBySelection(selection) {
return CONFIG.characters.find(function(character) {
return Number(character.id) === Number(selection);
});
}
function characterIndex(character) {
return CONFIG.characters.indexOf(character);
}
function totalCardsWidth() {
const count = CONFIG.characters.length;
return count * CONFIG.layout.boxWidth + (count - 1) * CONFIG.layout.spacingX;
}
function computedStartX() {
const total = totalCardsWidth();
return Math.max(10, Math.floor((Graphics.boxWidth - total) / 2));
}
function characterX(character) {
if (character.x !== undefined) {
return Number(character.x);
}
return computedStartX() + characterIndex(character) * (CONFIG.layout.boxWidth + CONFIG.layout.spacingX);
}
function characterY(character) {
if (character.y !== undefined) {
return Number(character.y);
}
return CONFIG.layout.startY;
}
function className(classId) {
if ($dataClasses && $dataClasses[classId]) {
return $dataClasses[classId].name;
}
return "Class ID: " + classId;
}
function removeSprite(sprite) {
if (sprite && sprite.parent) {
sprite.parent.removeChild(sprite);
}
}
function clearSelectionLayer() {
for (const container of _characterContainers) {
removeSprite(container);
}
_characterContainers = [];
removeSprite(_backgroundSprite);
_backgroundSprite = null;
removeSprite(_rootLayer);
_rootLayer = null;
}
function createRootLayer(scene) {
clearSelectionLayer();
_rootLayer = new Sprite();
scene.addChild(_rootLayer);
}
function fitBackgroundSprite(sprite) {
if (!sprite || !sprite.bitmap || sprite.bitmap.width <= 0 || sprite.bitmap.height <= 0) {
return;
}
const screenW = Graphics.boxWidth;
const screenH = Graphics.boxHeight;
const baseScaleX = screenW / sprite.bitmap.width;
const baseScaleY = screenH / sprite.bitmap.height;
const baseScale = Math.max(baseScaleX, baseScaleY);
const extraScale = Number(CONFIG.background.extraScale || 1);
const scale = baseScale * extraScale;
sprite.scale.x = scale;
sprite.scale.y = scale;
let x = Math.floor((screenW - sprite.bitmap.width * scale) / 2) + Number(CONFIG.background.offsetX || 0);
let y = Math.floor((screenH - sprite.bitmap.height * scale) / 2) + Number(CONFIG.background.offsetY || 0);
const scaledW = sprite.bitmap.width * scale;
const scaledH = sprite.bitmap.height * scale;
if (scaledW > screenW) {
const minX = screenW - scaledW;
const maxX = 0;
x = Math.max(minX, Math.min(maxX, x));
} else {
x = Math.floor((screenW - scaledW) / 2);
}
if (scaledH > screenH) {
const minY = screenH - scaledH;
const maxY = 0;
y = Math.max(minY, Math.min(maxY, y));
} else {
y = Math.floor((screenH - scaledH) / 2);
}
sprite.x = Math.floor(x);
sprite.y = Math.floor(y);
}
function createBackground() {
if (!_rootLayer || !CONFIG.background.enabled) {
return;
}
removeSprite(_backgroundSprite);
_backgroundSprite = null;
if (hasImageName(CONFIG.background.image)) {
const sprite = new Sprite();
sprite.bitmap = ImageManager.loadPicture(CONFIG.background.image);
sprite.opacity = Number(CONFIG.background.opacity || 255);
sprite.x = 0;
sprite.y = 0;
sprite.bitmap.addLoadListener(function() {
if (CONFIG.background.autoFitScreen) {
fitBackgroundSprite(sprite);
}
});
_backgroundSprite = sprite;
_rootLayer.addChild(_backgroundSprite);
} else {
const bitmap = new Bitmap(Graphics.boxWidth, Graphics.boxHeight);
bitmap.fillRect(
0,
0,
Graphics.boxWidth,
Graphics.boxHeight,
CONFIG.background.fallbackColor || "rgba(0,0,0,0.80)"
);
_backgroundSprite = new Sprite(bitmap);
_backgroundSprite.x = 0;
_backgroundSprite.y = 0;
_rootLayer.addChild(_backgroundSprite);
}
}
function makeCardFrameBitmap(character, selected) {
const w = CONFIG.layout.boxWidth;
const h = CONFIG.layout.boxHeight;
const textPanelHeight = CONFIG.layout.textPanelHeight;
const bitmap = new Bitmap(w, h);
const borderColor = selected ? "#ffe600" : "#b0b0b0";
const panelColor = selected ? "rgba(0,0,0,0.62)" : "rgba(0,0,0,0.52)";
const titleColor = selected ? "#ffe600" : "#ffffff";
bitmap.fillRect(0, 0, w, h, selected ? "rgba(0,0,0,0.00)" : "rgba(0,0,0,0.05)");
bitmap.fillRect(0, h - textPanelHeight, w, textPanelHeight, panelColor);
bitmap.fillRect(0, 0, w, 4, borderColor);
bitmap.fillRect(0, h - 4, w, 4, borderColor);
bitmap.fillRect(0, 0, 4, h, borderColor);
bitmap.fillRect(w - 4, 0, 4, h, borderColor);
const nameY = h - textPanelHeight + 8;
const classY = h - textPanelHeight + 36;
const selY = h - textPanelHeight + 55;
bitmap.fontSize = CONFIG.layout.nameFontSize;
bitmap.textColor = titleColor;
bitmap.outlineColor = "rgba(0,0,0,0.95)";
bitmap.outlineWidth = 5;
bitmap.drawText(character.actorName || ("Character " + character.id), 0, nameY, w, 26, "center");
bitmap.fontSize = CONFIG.layout.infoFontSize;
bitmap.textColor = "#ffffff";
bitmap.outlineColor = "rgba(0,0,0,0.95)";
bitmap.outlineWidth = 4;
bitmap.drawText(className(character.classId), 0, classY, w, 20, "center");
if (selected) {
bitmap.fontSize = CONFIG.layout.infoFontSize;
bitmap.textColor = "#ffe600";
bitmap.drawText("Selected", 0, selY, w, 18, "center");
}
return bitmap;
}
function fitImageToCard(sprite, bitmap) {
const boxW = CONFIG.layout.boxWidth;
const boxH = CONFIG.layout.boxHeight;
const pad = Number(CONFIG.layout.imagePadding || 0);
const availableW = Math.max(boxW - pad * 2, 1);
const availableH = Math.max(boxH - pad * 2, 1);
const scaleX = availableW / bitmap.width;
const scaleY = availableH / bitmap.height;
let scale;
if (CONFIG.layout.fitMode === "contain") {
scale = Math.min(scaleX, scaleY);
} else {
scale = Math.max(scaleX, scaleY);
}
scale *= Number(CONFIG.layout.imageZoom || 1);
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
sprite.scale.x = scale;
sprite.scale.y = scale;
sprite.x = Math.floor(boxW / 2);
sprite.y = Math.floor(boxH / 2);
}
function createCardMask() {
const mask = new PIXI.Graphics();
const pad = Number(CONFIG.layout.imagePadding || 0);
const w = CONFIG.layout.boxWidth - pad * 2;
const h = CONFIG.layout.boxHeight - pad * 2;
mask.beginFill(0xffffff);
mask.drawRect(pad, pad, w, h);
mask.endFill();
return mask;
}
function makeTrueGrayFilter() {
const filter = new PIXI.filters.ColorMatrixFilter();
filter.matrix = [
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0, 0, 0, 1, 0
];
return filter;
}
function makeBrightnessContrastFilter(brightness, contrast) {
const filter = new PIXI.filters.ColorMatrixFilter();
filter.brightness(Number(brightness || 1.0), false);
filter.contrast(Number(contrast || 1.0), true);
return filter;
}
function applyImageVisual(sprite, selected) {
if (typeof PIXI !== "undefined" && PIXI.filters && PIXI.filters.ColorMatrixFilter) {
if (selected) {
const filter = makeBrightnessContrastFilter(
CONFIG.layout.selectedBrightness,
CONFIG.layout.selectedContrast
);
sprite.filters = [filter];
} else {
const filters = [];
if (CONFIG.layout.inactiveBlackAndWhite) {
filters.push(makeTrueGrayFilter());
}
filters.push(makeBrightnessContrastFilter(
CONFIG.layout.inactiveBrightness,
CONFIG.layout.inactiveContrast
));
sprite.filters = filters;
}
} else {
sprite.filters = null;
}
}
function applyCardScale(container, selected) {
const selectedScale = Number(CONFIG.layout.selectedCardScale || 1.0);
const inactiveScale = Number(CONFIG.layout.inactiveCardScale || 1.0);
const scale = selected ? selectedScale : inactiveScale;
container.scale.x = scale;
container.scale.y = scale;
const w = CONFIG.layout.boxWidth;
const h = CONFIG.layout.boxHeight;
container.pivot.x = w / 2;
container.pivot.y = h / 2;
const character = container._moyCharacter;
container.x = characterX(character) + w / 2;
container.y = characterY(character) + h / 2;
}
function createCharacterContainer(character) {
const container = new Sprite();
container.x = characterX(character);
container.y = characterY(character);
container._moyCharacter = character;
container._moyImageSprite = new Sprite();
container.addChild(container._moyImageSprite);
container._moyMask = createCardMask();
container.addChild(container._moyMask);
container._moyImageSprite.mask = container._moyMask;
container._moyFrameSprite = new Sprite();
container.addChild(container._moyFrameSprite);
_rootLayer.addChild(container);
return container;
}
function refreshCharacterContainer(container, selected) {
const character = container._moyCharacter;
const imageName = selected
? (character.activeImage || "")
: (character.inactiveImage || character.activeImage || "");
applyCardScale(container, selected);
container._moyFrameSprite.bitmap = makeCardFrameBitmap(character, selected);
if (hasImageName(imageName)) {
const bitmap = ImageManager.loadPicture(imageName);
container._moyImageSprite.bitmap = bitmap;
container._moyImageSprite.visible = true;
container._moyImageSprite.opacity = selected ? 255 : Number(CONFIG.layout.inactiveOpacity || 170);
applyImageVisual(container._moyImageSprite, selected);
bitmap.addLoadListener(function() {
fitImageToCard(container._moyImageSprite, bitmap);
});
} else {
container._moyImageSprite.bitmap = null;
container._moyImageSprite.visible = false;
container._moyImageSprite.filters = null;
}
}
function createSelectionUI(scene) {
createRootLayer(scene);
createBackground();
_characterContainers = CONFIG.characters.map(function(character) {
return createCharacterContainer(character);
});
refreshSelectionUI(true);
}
function refreshSelectionUI(force) {
if (!_rootLayer) {
return;
}
const selection = currentSelection();
if (!force && selection === _lastSelection) {
return;
}
_lastSelection = selection;
for (const container of _characterContainers) {
const character = container._moyCharacter;
const selected = Number(character.id) === Number(selection);
refreshCharacterContainer(container, selected);
}
}
function removeAllPartyMembers() {
const members = $gameParty.members().slice();
for (const member of members) {
$gameParty.removeActor(member.actorId());
}
}
function applyCharacter(character) {
if (!character) {
console.error("[MoyCharacterSelector] Character configuration not found.");
SoundManager.playBuzzer();
return false;
}
const actorId = Number(character.actorId || 0);
const classId = Number(character.classId || 0);
if (!$dataActors[actorId]) {
console.error("[MoyCharacterSelector] Invalid Actor ID:", actorId);
SoundManager.playBuzzer();
return false;
}
if (!$dataClasses[classId]) {
console.error("[MoyCharacterSelector] Invalid Class ID:", classId);
SoundManager.playBuzzer();
return false;
}
if (CONFIG.clearPartyBeforeAdd) {
removeAllPartyMembers();
}
const actor = $gameActors.actor(actorId);
if (!actor) {
console.error("[MoyCharacterSelector] Failed to load actor:", actorId);
SoundManager.playBuzzer();
return false;
}
if (character.actorName) {
actor.setName(character.actorName);
}
actor.changeClass(classId, CONFIG.keepLevelOnClassChange);
actor.refresh();
$gameParty.addActor(actorId);
$gamePlayer.refresh();
return true;
}
function moveLeft() {
let selection = currentSelection() - 1;
if (selection < 1) {
selection = maxSelection();
}
$gameVariables.setValue(CONFIG.selectionVariableId, selection);
SoundManager.playCursor();
_lastSelection = 0;
refreshSelectionUI(true);
}
function moveRight() {
let selection = currentSelection() + 1;
if (selection > maxSelection()) {
selection = 1;
}
$gameVariables.setValue(CONFIG.selectionVariableId, selection);
SoundManager.playCursor();
_lastSelection = 0;
refreshSelectionUI(true);
}
function finishSelection() {
clearSelectionLayer();
if (CONFIG.transparentPlayerDuringSelection) {
$gamePlayer.setTransparent(false);
}
if (CONFIG.doneSwitchId > 0) {
$gameSwitches.setValue(CONFIG.doneSwitchId, true);
}
_active = false;
}
function confirmSelection() {
const character = characterBySelection(currentSelection());
const success = applyCharacter(character);
if (!success) {
return;
}
SoundManager.playOk();
finishSelection();
if (CONFIG.transfer.enabled) {
$gamePlayer.reserveTransfer(
Number(CONFIG.transfer.mapId || 1),
Number(CONFIG.transfer.x || 0),
Number(CONFIG.transfer.y || 0),
Number(CONFIG.transfer.direction || 2),
Number(CONFIG.transfer.fadeType || 0)
);
}
}
MoyCharacterSelector.start = function() {
if (CONFIG.doneSwitchId > 0 && $gameSwitches.value(CONFIG.doneSwitchId)) {
return;
}
_active = true;
_lastSelection = 0;
if (currentSelection() < 1) {
$gameVariables.setValue(CONFIG.selectionVariableId, 1);
}
if (CONFIG.transparentPlayerDuringSelection) {
$gamePlayer.setTransparent(true);
}
if (SceneManager._scene && SceneManager._scene instanceof Scene_Map) {
createSelectionUI(SceneManager._scene);
}
};
MoyCharacterSelector.stop = function() {
finishSelection();
};
MoyCharacterSelector.refresh = function() {
_lastSelection = 0;
refreshSelectionUI(true);
};
MoyCharacterSelector.config = function() {
return CONFIG;
};
SistemaSelecao.iniciar = function() {
MoyCharacterSelector.start();
};
SistemaSelecao.parar = function() {
MoyCharacterSelector.stop();
};
SistemaSelecao.config = function() {
return CONFIG;
};
const _Scene_Map_update = Scene_Map.prototype.update;
Scene_Map.prototype.update = function() {
_Scene_Map_update.call(this);
if (!_active) {
return;
}
if (!_rootLayer) {
createSelectionUI(this);
}
if (Input.isTriggered("left")) {
moveLeft();
return;
}
if (Input.isTriggered("right")) {
moveRight();
return;
}
if (Input.isTriggered("ok")) {
confirmSelection();
return;
}
refreshSelectionUI(false);
};
})();
Créditos
Plugin por Moycanow.Pode usar em projetos comerciais e não comerciais.
Pode editar conforme a necessidade do projeto.
Se redistribuir, mantenha os créditos.
