[Programación] Desbloqueables

marod

Hola a todos.

Tengo una duda respecto a la programación de los desbloqueables de un juego y es básicamente en que no sé como se diseña. Y el código que yo pienso que es se me asemeja a una guarrada tremenda.

Os voy a relatar el como yo pienso que se programaría: Imaginaos que tengo 10 skins en mi juego, solo puedo activar 1 a la vez. Lo primero que creo que debería hacer es crearme una clase llamada coloquialmente "toolbox" (Lo he leido en la wiki de Unity), se trata de un singleton donde almacenare las variables globales para saber qué skins tengo desbloqueadas y cual de ellas tengo activada. Una vez hecho esto, en el Game controller de mi juego, verificar cual está activada y SUPONGO y esta es la parte que creo que me parece una guarrada como inexperto del tema, tener un switch con cada caso por skin, donde cogeré las cosas que hagan falta para cambiar la skin del juego.

¿Como veis ese diseño? ¿Qué haríais vosotros?

Un saludo y gracias.

B

Soy muy nuevo programando así que igual digo una tontería, pero.. ¿no se puede hacer un array con todos los debloqueables y valores 0 y 1 según lo tengas o no?

1 respuesta
marod

#2 El coste que supondría iterar el array para saber que desbloqueable es, y si está o no activado, creo que sería demasiado grande. Creo que mejor variables globales. Sí me equivoco, corregidme.

2 respuestas
varuk

#1 No sé si te sirve, nunca he programado en Unity pero si el problema es la guarrada del switch... quizás la solución sea una jerarquía de clases, con una clase padre y luego subclases para los distintos skins donde cada clase de skin pues guarda una variable "isActivate" a true o false y después con un patrón estrategia, por ejemplo, y el polimorfismo pues ahorrarte el switch. Estoy pensando en plan Programación Orientada a Objetos, así rápido, no sé si valdrá o no.

1 respuesta
BLZKZ

#4 para qué quieres 50 clases de skin cuando puedes tener 50 objetos de la clase skin? y sigues con el problema de donde almacenarlo

1 respuesta
cabron

#3

http://jsfiddle.net/pmv3k799/

En 1 segundo da tiempo a iterar y escribir cada elemento de un array de un millón de elementos más de 600 veces, y eso que hablamos de la máquina virtual js de un navegador que no es precisamente el entorno más rápido, para que te hagas una idea del 'gran coste' de iterar un array.

1 2 respuestas
varuk

#5 mmmm no sé exactamente a que se refiere con skin pero he supuesto que cada skin tiene sus propiedades y con una jerarquía ordenada será más facil de sobrellevar luego.

1 respuesta
BLZKZ

#7 y yo imagino que cada skin tendrá las mismas propiedades con diferentes valores (suele ser lo lógico, para eso sirve la abstracción xD)

1
B

Puedes almacenarlo con PlayerPrefs: http://docs.unity3d.com/ScriptReference/PlayerPrefs.html

Hace un porrón hice un sistema de desbloqueables para un juego para android y lo que hacía era generar una key única para cada desbloqueable y guardarlo usando PlayerPrefs, luego es muy cómodo coger y consultar si tienes bloqueado o desbloqueado algo.

Al puro estilo de:

PlayerPrefs.SetInt("Key Generada", 0);
if(PlayerPrefs.GetInt("Key Generada") == 0)
// no está desbloqueado
else
// sí lo está.

3 respuestas
B

#9 Eso son switch :D

1 respuesta
Kiroushi

#6 Me parece una forma bastante ruda de cuantificar el "coste" de iterar el array.

No es lo mismo iterar un array vacío y asignar su valor, que iterar un array de objetos.

Dependiendo de cuán optimizado esté el diseño de la clase del objeto en cuestión puede que sea insignificante o puede que sea un consumo ridículo de recursos.

La cosa es que a día de hoy, como quien dice, "la potencia de computación es infinita pero el tiempo no". La optimización ha caído en el olvido.

Mi consejo sería tener un objeto "partida" que tenga una propiedad de tipo array que marque las skins que estén desbloqueadas. Digamos por ejemplo:

['u125', 'z284', 'm342']

Iteras sobre ese array, cuyo coste de rendimiento sí que es despreciable, y luego llamas al array:

skins[partida.skinsDesbloqueados[y]]

Donde y es el índice de iteración del primer array (he tenido que cambiar "i" porque me lo sustituye por cursiva en MV).

Aún así, en loops lo primero que haría sería comprobar si el índice de la iteración existe en el array de skins, por si las moscas.

4 respuestas
B

#10 Es que es la forma más sencilla de hacerlo.. a lo mucho haría una clase para tener separado todo ese código y que pudiese consultar los debloqueables pasando la key como parámetro para un método y así se olvida, pero no entiendo qué hay de malo en usar un switch..

1 respuesta
BLZKZ

#12 yo muy bien programar

Midgard: me podeis decir si esto es correcto?: GameObject.Find("PlayerCommonLight" ).GetComponent(SmoothFollow).target = spotlightTarget;
eisen: sabes lo que significan esos puntos?
Midgard: creo que enlazan una línea, no?
Midgard: no, no tengo puta idea

1 respuesta
B

#11 Pero eso genera otro problema.

¿Se pueden desbloquear skins durante la partida? Porque si o si, tendrás que recorrer los skins para junto con los que ya tienes desbloqueados, te salgan los que no.

2 respuestas
B

#13 Pon la fecha de eso también so puta xD

marod

#14 En teoría se desbloquearían al acabar la partida.

Seguramente me decante por lo que dice #9 con los playerprefs, aunque lo que dice #11 también me lo planteo. Lo veo "sencillo" de implementar, aunque por ahora no tenga ni puta idea de como vaya a hacerlo.

Veré como lo hago. Muchas gracias a todos por contestar mi duda :D

1
Kiroushi

#14 Hombre, creo que por descontado hay que considerar una iteración del array de skins del juego si quieres listarlos xD

De todas formas este problema la única solución que tiene es hacer debug con el diseño de #1 y ver qué solución es la más adecuada.

Si sólo quiere listar skins desbloqueados, con el método que le propongo se ahorra iterar el array de skins al completo.

Si quiere listarlos todos y ver cuál está desbloqueado o no, sólo tiene que hacer un bucle del array de skins y comprobar si existe el índice en el array de skins desbloqueados.

Haz prueba de rendimiento usando la propiedad "desbloqueado" en tu clase "skin", y comprobando si es true o false en la iteración, o usando el método que te planteo en #11. Mira cuál te consume menos recursos y a correr.

1 respuesta
marod

#17 Para que os hagáis una idea, mi intención es hacer un slider con los nombres de los skins desbloqueados y que se activen al elegirlos. Osea que lo mas probable es que acabe usando un Array

1 respuesta
Kiroushi

#18 ¿Cuántos skins piensas tener en total en el juego?

Si vas a tener 10, la pregunta no tiene ni sentido. Utilizas el array original y listo. El rendimiento ni lo vas a notar.

Si vas a tener 400, me plantearía usar un segundo array donde no solamente guarde el índice del desbloqueado, si no también el nombre u otras propiedades a mostrar en plan "preview".

1 respuesta
cabron

#11

"No es lo mismo iterar un array vacío y asignar su valor, que iterar un array de objetos."

Te equivocas, es exáctamente lo mismo. Iterar un array es leer direcciones de memoria consecutivas, es indiferente que la dirección de memoria apunte a una región que usa 1byte o 100mb.

El motivo por el que he escrito algo en cada posición es para evitar que la máquina virtual haga algún tipo de optimización, ya que es común hoy día que los compiladores/interpretes detecten instrucciones que no tienen efectos secundarios y se las salten.

Iterar un array va a tener siempre una complejidad de O(n) sea cual sea el contenido del array, ese ejemplo era puramente anecdótico para poner unos números que demuestren que iterar un array se hace en tiempos irrosorios y que no tiene sentido preocuparse por el tiempo que se tarda en el 90% de las veces.

2 1 respuesta
marod

#19 No se cuantas, pero no más de 2 o 3. Al fin y al cabo lo podría haber hecho con un switch, pero quiero aprender a hacerlo bien por si en algún casual tuviera que hacer algo que implementara 1000 skins.

Entonces, si tengo 50 skins en un array, luego tengo que crearme una clase con 50 ifs en el que cada cual me cambie los sprites?

1 respuesta
Srednuht

#21 Para arrays más grandes..Si te montas quizá un sistema de códigos ( id's ) para los objetos, podrías utilizar algoritmos de ordenación y búsqueda. Por ejemplo, tu clasificas tal que así:

TipoObjeto -> [Rango de id's]

Armas -> [0-2304]
Escudos -> [2305 - 5045]
Penes -> [ 5045 - 8034]

Ahora imagina que cada personaje tiene un array que va de 0 a 8034 que contiene únicamente las ids de los objetos desbloqueados. Pongamos que inicialmente está vacío.

Para introducir los elementos que vas desbloqueando, utilizas un sistema de ordenación, de tal forma que, si el player desbloquease los objetos '4056', '347' y '9340', en lugar de tener un array tal que asi:

desbloqueados{4056,347,9340}

automáticamente te lo ordenase:

desbloqueados{347,4056,9340}

De esta forma tienes un array ordenado al que para realizar las consultas de si está o no desbloqueado, puedes preguntarle por una id en concreto y comprobar si existe en desbloqueados.

Para arrays pequeños no tiene tampoco mucho uso, dado que el método de búsqueda secuencial es más que asumible en coste. Pero si tuvieras arrays más grandes, utilizando esa id y un método de búsqueda, lo tendrías solucionado en nanomicromilipenisegundos ( la respuesta de si está desbloqueado o no, vamos, si existe esa id en el array 'desbloqueados' )

RPV: Por un lado, al desbloquearlos los insertas en el array 'desbloqueados' con un método de ordenación. Y al consultar si está o no en dicha lista un elemento en concreto tiras de un algoritmo de búsqueda que trabaje sobre arrays ordenados

PD: Pero vaya, que secuencial rulz si no es para arrays gigantes, ni te molestes xD, te lo he comentado por lo de '...si algún día tuviera que...'

1 respuesta
Kaiserlau

Justo la semana pasada estaba con pygame y creeme #1 xD apartir de una lista de 10k objetos empezaba a tener problema de rendimiento en mi caso, tambien hay q decir que en cada iteracion se hacia llamada a un metodo de cada objeto para hacer un calculo y suma de vectores.

En cuanto al asco del "switch", si no lo integras en el objeto "skin" como propiedad (True,False) o con una subclase "mochila" q te guarde el skin con esa propiedad xd, lo vas a tener q usar de alguna manera al iterar tu array.

pd: me molan estos threads

1 respuesta
marod

#22 Me la apunto para un futuro.

Ahora la duda ya no es, saber si están desbloqueados o no, la duda es DONDE cargo los que tengo desbloqueados, porque no creo que estaría bien tener una clase con 1000 ifs o un Switch con 1000 casos, uno por cada skin.

Quiero decir, una clase donde busque el que tenga activado y empiece if tal activame estos sprites, if tal activame estos otros...

#23 Qué los ordenadores en comparación con otros años son mucho más potentes, pero creo que si los fuerzas... al final pues el rendimiento se nota.

Gracias a todos

2 respuestas
Kaiserlau

#24 Si le metes una propiedad "estado = False" (inicias los objetos como bloqueados) a la clase padre "skin" con un metodo q pueda cambiar "estado", no vas a tener q crear un if por cada tipo, siempre y cuando todos los skins compartan ese mismo objeto. Luego iteras todo el catalogo metiendolos en una lista y luego ya pones las reglas q quieras en el controlador, como te dice en la wiki q has leido puedes tener una clase "mochila" o toolbox con las reglas q quieras.

En cuanto al tema de rendimiento la unica manera q vas a poder probar las muchas soluciones q le puedes dar a es problema va a ser jugando con magnitudes muy grandes para empezar a notarlo.

B

Tan dificil es tener un atributo "char" en tu clase para decir si está bloqueado o no?

La iteración es tan sencilla y rápida cómo dice #6 y/o #20. Te creas un vector cuyos miembros sean las direcciones de los objetos creados de esa clase. Y ya creas tu lógica a partir de ahí.

Si tienes 1000 logros, 1000 skins, 1000 lo que sea y quieres una lógica distinta para cada "lo que sea", no te queda otra que picartela a pelo, de eso no te salva nadie.

FernandoA

Hagas como hagas, no lo guardes como dice #9. Los PlayerPrefs son muy cómodos para guardar datos, pero solo últiles (como su nombre indica) para guardar preferencias de usuario. Entiéndase como resolución, opciones gráficas, volumen, música..., cosas de ese estilo. Los PlayerPrefs son fácilmente editables por terceros (se pueden modificar como si fueran un archivo de texto) con lo que, salvo que quieras que te desbloqueen los skins por otra vía, no almacenes ahí su estado.

Para guardar los datos de la partida, avance en el juego, objetos de inventario, posición en el mapa (creo que se entiende, todo lo relacionado con el juego en sí) y los objetos desbloqueables ya sea mediante méritos jugando o mediante compra ingame utiliza como mínimo archivos binarios. Para más seguridad puedes ofuscarlos y guardar valores de integridad en el archivo. Lo ideal sería encriptarlos.

B

#3 ¿Te refieres a coste humano o del computador?
¿Por qué no vale mi método?
Sería algo como
bool[] skinsDesbloqueadas= new bool[20];
Y cuando la desbloquees cambias el valor:
skinsDesbloqueadas[n] = true;
Luego si quieres iterar con "foreach" para bloquear las otras ¿es más costoso que escribir switchs?
Repito que soy muy newbie, no os ensañéis demasiado conmigo si queréis responder.

Soltrac

#11 Acabas de escribir una barbaridad.... un array de objetos son un conjunto de direcciones de memoria que apuntan cada una de ellas a una dirección de memoria de un objeto tan grande como una clase Skin o tan pequeño como un byte, recorrerlos es lo mismo.

Y por cierto, si estais usando Unity, estais usando C# y aprovechad el maldito lenguaje (LINQ para hacer búsquedas en una lista)

Con lo cómodo que es.......

 ListaDeSkins.First(x => x.AtributoDeSkin == true || x.Atributo2DeSkin == 3) 

Una línea de código limpita para no tener que usar un foreach.

1 3 respuestas
Kiroushi

#29 Tal vez me he expresado mal:

Lo que quería decir es que no es lo mismo iterar un array vacío y asignar un valor (como el ejemplo que puso cabron), que iterar un array de objetos con propiedades y métodos para hacer llamadas de estos y actuar en consecuencia.

1 respuesta

Usuarios habituales