Estoy buscando una solución para el comportamiento de "Opciones de clic derecho".
Básicamente, cualquier elemento de un juego, cuando se hace clic con el botón derecho, puede mostrar un conjunto de opciones en función del objeto que sea.
Ejemplos de clic derecho para diferentes escenarios :
Inventario: el casco muestra opciones (equipar, usar, soltar, descripción)
Banco: el casco muestra opciones (Take 1, Take X, Take All, Descripción)
Piso: el casco muestra opciones (tomar, caminar aquí, descripción)
Obviamente, cada opción apunta de alguna manera a un cierto método que hace lo que dice. Esto es parte del problema que estoy tratando de resolver. Con tantas opciones de potencia para un solo elemento, ¿cómo diseñaría mis clases de tal manera que no sean extremadamente desordenadas?
- He pensado en la herencia, pero eso podría ser muy largo y la cadena podría ser enorme.
- He pensado en usar interfaces, pero esto probablemente me restringiría un poco, ya que no podría cargar datos de elementos de un archivo Xml y colocarlos en una clase genérica de "Elementos".
Estoy basando mi resultado final deseado en un juego llamado Runescape. Se puede hacer clic derecho en cada objeto en el juego y, según lo que sea y dónde esté (inventario, piso, banco, etc.) muestra un conjunto diferente de opciones disponibles para que el jugador interactúe.
¿Cómo haría para lograr esto? En primer lugar, qué enfoque debo tomar, decidir qué opciones DEBERÍAN mostrarse y, una vez que se haga clic, cómo llamar al método correspondiente.
Estoy usando C # y Unity3D, pero cualquier ejemplo proporcionado no tiene que estar relacionado con ninguno de ellos, ya que busco un patrón en lugar del código real.
Cualquier ayuda es muy apreciada y si no he sido claro en mi pregunta o en los resultados deseados, publique un comentario y lo atenderé lo antes posible.
Esto es lo que he intentado hasta ahora:
- De hecho, he logrado implementar una clase genérica de "Objeto" que contiene todos los valores para diferentes tipos de artículos (ataque adicional, defensa adicional, costo, etc.). Estas variables se rellenan con datos de un archivo Xml.
- He pensado en colocar todos los métodos de interacción posibles dentro de la clase Item, pero creo que esta es una forma increíblemente desordenada y pobre. Probablemente he tomado el enfoque equivocado para implementar este tipo de sistema al usar solo una clase y no subclasificar a diferentes elementos, pero es la única forma en que puedo cargar los datos de un Xml y almacenarlos en la clase.
- La razón por la que elegí cargar todos mis artículos desde un archivo Xml se debe a que este juego tiene la posibilidad de más de 40,000 artículos. Si mis cálculos son correctos, una clase para cada artículo es muchas clases.
fuente
Respuestas:
Como con todo en el desarrollo de software, no existe una solución ideal. Solo la solución ideal para usted y su proyecto. Aquí hay algunos que podrías usar.
Opción 1: el modelo de procedimiento
El
antiguométodoobsoleto de lavieja escuela.Todos los elementos son tipos simples de datos antiguos sin ningún método, pero con muchos atributos públicos que representan todas las propiedades que un elemento podría tener, incluidas algunas banderas booleanas como
isEdible
,isEquipable
etc. , que determinan qué entradas del menú contextual están disponibles para él (tal vez también podría prescindir de estos indicadores cuando puede derivarlo de los valores de otros atributos). Tenga algunos métodos comoEat
,Equip
etc. en su clase de jugador que toma un elemento y que tiene toda la lógica para procesarlo de acuerdo con los valores de los atributos.Opción 2: el modelo orientado a objetos
Esta es más una solución OOP por libro que se basa en la herencia y el polimorfismo.
Tener una clase base
Item
de la que hereden otros elementos comoEdibleItem
,EquipableItem
etc. La clase base debe tener un método públicoGetContextMenuEntriesForBank
,GetContextMenuEntriesForFloor
etc. que devuelva una lista deContextMenuEntry
. Cada clase heredado anularía estos métodos para devolver las entradas del menú contextual que sean apropiadas para este tipo de elemento. También podría llamar al mismo método de la clase base para obtener algunas entradas predeterminadas que son aplicables para cualquier tipo de elemento. LaContextMenuEntry
habría una clase con un métodoPerform
que a su vez llama al método pertinente del artículo que lo creó (se puede utilizar un delegado para esto).Con respecto a sus problemas con la implementación de este patrón al leer datos del archivo XML: Primero examine el nodo XML para cada elemento para determinar el tipo de elemento, luego use un código especializado para cada tipo para crear una instancia de la subclase apropiada.
Opción 3: el modelo basado en componentes
Este patrón usa composición en lugar de herencia y está más cerca de cómo funciona el resto de Unity. Dependiendo de cómo estructura su juego, podría ser posible / beneficioso utilizar el sistema de componentes Unity para esto ... o no, su millaje puede variar.
Cada objeto de la clase
Item
tendría una lista de componentes comoEquipable
,Edible
,Sellable
,Drinkable
, etc. Un elemento puede tener uno o ninguno de cada componente (por ejemplo, un casco de chocolate sería a la vezEquipable
yEdible
, y cuando no es una trama crítica artículo de búsqueda tambiénSellable
). La lógica de programación que es específica del componente se implementa en ese componente. Cuando el usuario hace clic derecho en un elemento, los componentes del elemento se repiten y se agregan entradas de menú contextual para cada componente que existe. Cuando el usuario selecciona una de estas entradas, el componente que agregó esa entrada procesa la opción.Puede representar esto en su archivo XML al tener un subnodo para cada componente. Ejemplo:
fuente
Entonces, Mike Hunt, tu pregunta me interesó tanto que decidí implementar una solución completa. Después de tres horas de probar cosas diferentes, terminé con esta solución paso a paso:
(Tenga en cuenta que este NO ES un código muy bueno, por lo que aceptaré cualquier edición)
Crear panel de contenido
(Este panel será un contenedor para nuestros botones de menú contextual)
UI Panel
anchor
en la parte inferior izquierdawidth
en 300 (como desee)Vertical Layout Group
y configúreloChild Alignment
en el centro superior,Child Force Expand
en ancho (no en altura)Content Size Fitter
yVertical Fit
configúrelo en Tamaño mínimo(En este punto, nuestro Panel se reducirá a una línea. Es normal. Este panel aceptará botones como elementos secundarios, los alineará verticalmente y se estirará hasta la altura del contenido de resumen)
Crear botón de muestra
(Este botón se instanciará y personalizará para mostrar elementos del menú contextual)
anchor
en la parte superior izquierdaLayout Element
, establecidoMin Height
en 30,Preferred Height
en 30Crear script ContextMenu.cs
(Este script tiene un método que crea y muestra el menú contextual)
ContentPanel
prefab a la ranura correspondiente, y arrastre Canvas mismo a la ranuraCanvas
.Crear script de ItemController.cs
Cube
), colóquelo para que sea visible para la cámara y adjunte este script a él. Arrastrar y soltarsampleButton
prefabricado a la ranura correspondiente.Ahora, intenta ejecutarlo. Cuando hace clic con el botón derecho en el objeto, debe aparecer el menú contextual, completado con la lista que hicimos. Al presionar los botones se imprimirá en la consola algo de texto, y el menú contextual se destruirá.
Posibles mejoras:
Proyecto de muestra (Unity Personal 5.2.0, VisualStudio Plugin): https://drive.google.com/file/d/0B7iGjyVbWvFwUnRQRVVaOGdDc2M/view?usp=sharing
fuente