User:Anonimzwx/Dyzen Sprite Maker (Spanish): Difference between revisions
No edit summary |
|||
(35 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Dyzen Sprite Maker es un tool creado por Anonimzwx perteneciente a SNES Office que facilita la creación de enemigos (o cualquier objeto que use tiles de la OAM), originalmente fue hecho para el videojuego Super Mario World, pero hay planes para que funcione con otros videojuegos. | Dyzen Sprite Maker es un tool creado por Anonimzwx perteneciente a SNES Office que facilita la creación de enemigos (o cualquier objeto que use tiles de la OAM), originalmente fue hecho para el videojuego Super Mario World, pero hay planes para que funcione con otros videojuegos. | ||
Line 8: | Line 6: | ||
* Creación de animaciones. | * Creación de animaciones. | ||
* Creación de cajas de colisión. | * Creación de cajas de colisión. | ||
* | * Generador automático de código ASM con editor. | ||
== Video-Tutoriales == | == Video-Tutoriales == | ||
Line 31: | Line 30: | ||
'''Actualmente se busca gente que pueda documentar el sistema de enemigos otros videojuegos para darles soporte''' | '''Actualmente se busca gente que pueda documentar el sistema de enemigos otros videojuegos para darles soporte''' | ||
== Variables == | |||
Para comprender como funciona el sistema de animaciones debemos comprender el como esta modelado. Para esto debemos separarlo en 2 partes: | |||
=== Frames === | |||
Un sprite generado por Dyzen, contiene un conjunto de frames. Cada frame posee sus propias hitboxes, de esta manera al elegir un frame, se selecciona la interacción que posee. Además cada frame tiene un ID, que sirve para poder seleccionarlo. El frame seleccionado es el que es mostrado por el sprite. | |||
Para seleccionar un frame, utilizamos la variable <code>!FrameIndex,x</code>, si le ponemos ID del frame que deseamos, el sprite automáticamente mostrara ese ID, aquí un ejemplo de como hacerlo: | |||
<pre> | |||
LDA #$ID_Del_Frame_Deseado | |||
STA !FrameIndex,x | |||
</pre> | |||
Si utilizas el sistema de animaciones generados por el tool, entonces no necesitas cambiar esta variable normalmente. | |||
El frame mostrado también puede ser volteado horizontal o verticalmente, para esto utilizamos la variable <code>!GlobalFlip,x</code>, los valores que admite son: | |||
* #$00 = Sin volteo | |||
* #$01 = Volteo horizontal | |||
* #$02 = Volteo vertical | |||
* #$03 = Volteo horizontal y vertical. | |||
Un ejemplo de uso es: | |||
<pre> | |||
LDA #$01 | |||
STA !GlobalFlip,x | |||
</pre> | |||
Esto voltearia al sprite horizontalmente. | |||
=== Animaciones === | |||
Una animación es un subconjunto de frames del conjunto total que contiene el sprite. Esta animación recorre sus frames desde el primero hasta el ultimo, además existen las animaciones que se reproducen solo una vez (Only Once) y las que se reproducen de forma continua (Continuous). Cada animación tiene su propio ID. | |||
Para regular el tiempo entre cada frame de la animación, se utiliza la variable <code>!AnimationTimer,x</code>, esta variable indica la cantidad de loops para pasar al siguiente frame, por ejemplo, si el valor de esta variable fuera 2, entonces en 2 loops el frame cambiara al siguiente. | |||
<code>'''Nota:''' Cada loop dura 1/60 segundos.</code> | |||
Para regular el frame actual que es mostrado por la animación, utilizamos la variable <code>!AnimationFrameIndex,x</code>, esta variable contiene la posición del frame que es reproducido actualmente, las posiciones inician desde 0 . Por ejemplo, si la animación tuviera 4 frames y esta variable tuviera el valor 1, entonces, el frame mostrado seria el segundo. | |||
Para regular si el frame actual debe ser volteado, utilizamos <code>!LocalFlip,x</code>, esta variable es similar a <code>!GlobalFlip,x</code>, solo que esta solo afecta al frame actual que se reproduce, al cambiar de frame tomara otro valor, la utilidad de esta variable es que permite utilizar frames volteados dentro de la animación, que a su vez pueden ser volteados por el <code>!GlobalFlip,x</code>, para efectos practicos, en situaciones sumamente excepcionales requeriras utilizarla, asi que puedes ignorarla. | |||
Para seleccionar una animación, debemos utilizar la variable <code>!AnimationIndex,x</code>. Debemos tomar en cuenta que cuando cambiamos esta variable, debemos cambiar también <code>!AnimationFrameIndex,x</code>, <code>!AnimationTimer,x</code> y <code>!FrameIndex,x</code> (incluso en algunos casos el <code>!LocalFlip,x</code>). Por esta razón, el tool genera rutinas para iniciar cada animación desde el inicio que pueden ser llamadas, estas rutinas son las que tienen nombre del estilo <code>ChangeAnimationFromStart_<animation_name></code>, por ejemplo, si tengo una animacion que se llama "Animation1", la rutina que permite empezar esa animación desde el inicio es <code>ChangeAnimationFromStart_Animation1</code>. Podemos llamar estas rutinas, para cambiar la animación y que esta se reproduzca desde el primer frame de su lista de la siguiente manera: | |||
<pre> | |||
JSR ChangeAnimationFromStart_Animation1 | |||
</pre> | |||
Además, el tool genera una rutina llamada InitWrapperChangeAnimationFromStart la que requiere ser llamada en el Init del sprite. Esta rutina da la instrucción de hacer que el sprite comience con la animación de ID 0 cuando el sprite sea creado, si deseas iniciar desde otra animación puedes reemplazar la linea que dice: | |||
<pre> | |||
STZ !AnimationIndex,x | |||
</pre> | |||
Por: | |||
<pre> | |||
LDA #$ID_De_La_Animacion_Deseada | |||
STA !AnimationIndex,x | |||
</pre> | |||
Si de todas maneras, no quieres usar estos métodos, puedes siempre hacer el tuyo propio, para esto solo debes utilizar las variables antes mencionadas, por ejemplo, si deseas que la animación #$7 inicie desde el frame en la posición #$5 (que a su vez su ID es el #$10), que el primer frame tarde #$03 loops y que inicie volteado horizontalmente, puedes hacer lo siguiente: | |||
<pre> | |||
LDA #$07 | |||
STA !AnimationIndex,x ;Set the animation Index | |||
LDA #$05 | |||
STA !AnimationFrameIndex,x ;Set the current frame of the animation to show | |||
LDA #$10 | |||
STA !FrameIndex,x ;Set the ID of the frame to show | |||
LDA #$03 | |||
STA !AnimationTimer,x ;Set the number of loops that will be showed the frame | |||
LDA #$01 | |||
STA !LocalFlip,x ;Flip the frame | |||
</pre> | |||
También puedes chequear que el cambio de animación se haga justo cuando termina cierto frame de otra animación: | |||
<pre> | |||
ChangeToAnotherAnimation: | |||
LDA !AnimationIndex,x | |||
CMP #$ID_De_La_Animacion_Que_Se_Quiere_Cambiar | |||
BNE + | |||
LDA !AnimationFrameIndex,x | |||
CMP #$Posicion_Del_Frame | |||
BNE + | |||
LDA !AnimationTimer,x | |||
BNE + | |||
JSR ChangeAnimationFromStart_Name_Of_The_New_Animation | |||
+ | |||
RTS | |||
</pre> | |||
== Acciones para Hitbox == | == Acciones para Hitbox == | ||
Las acciones de las hitboxes son pequeños trozos de código que son ejecutados cuando cierta hitbox colisiona con el player. De momento solo funcionan con el player, a futuro se implementaran para interacción sprite<->sprite. Todas las siguientes acciones funcionan solo en el videojuego Super Mario World. | Las acciones de las hitboxes son pequeños trozos de código que son ejecutados cuando cierta hitbox colisiona con el player. De momento solo funcionan con el player, a futuro se implementaran para interacción sprite<->sprite. | ||
<code>'''Nota:''' Todas las siguientes acciones funcionan solo en el videojuego Super Mario World.</code> | |||
Antes que todo debemos considerar que el tool permite tener multiples hitboxes, debido a esto existen 2 tipos de acciones: | |||
La | La acción default es la que ejecutaria la mayoria de las cajas de colisión y que es autogenerada por el tool y que se veria asi: | ||
<pre> | <pre> | ||
Line 44: | Line 146: | ||
</pre> | </pre> | ||
<code>'''Nota:''' Existe un bug en que si editas la acción default desde el editor de código y vuelves a recargar el codigo, volvera a su estado inicial, por lo tanto, se recomienda editar esta en el archivo generado al hacer "Extract Resources" y no en el editor de codigo del tool.</code> | |||
Y las acciónes no default, que son creadas en el apartado de interacción por el usuario, por ejemplo: | |||
<pre> | <pre> | ||
Line 52: | Line 156: | ||
RTS | RTS | ||
</pre> | </pre> | ||
A continuación algunos códigos de ejemplo que pueden utilizarse, estos deben ser puestos en la zona que dice <code>;Here you can write your action code</code>: | A continuación algunos códigos de ejemplo que pueden utilizarse, estos deben ser puestos en la zona que dice <code>;Here you can write your action code</code>: | ||
Line 59: | Line 161: | ||
=== Dañar al player === | === Dañar al player === | ||
Daña al jugador cuando toca la | Daña al jugador cuando toca la hitbox. | ||
<div class="toccolours mw-collapsible mw-collapsed" style="width:800px; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}"> | <div class="toccolours mw-collapsible mw-collapsed" style="width:800px; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}"> | ||
Line 69: | Line 171: | ||
=== Matar al player === | === Matar al player === | ||
Mata al jugador cuando toca la | Mata al jugador cuando toca la hitbox. | ||
<div class="toccolours mw-collapsible mw-collapsed" style="width:800px; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}"> | <div class="toccolours mw-collapsible mw-collapsed" style="width:800px; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}"> | ||
Line 79: | Line 181: | ||
=== Interacción Default === | === Interacción Default === | ||
Si el player salta sobre la | Si el player salta sobre la hitbox, es impulsado para arriba, en otro caso es dañado. | ||
Puedes reemplazar <code>;Here you can call a routine to damage the sprite by normal jump</code> por alguna rutina para dañar o matar al sprite por salto normal. | Puedes reemplazar <code>;Here you can call a routine to damage the sprite by normal jump</code> por alguna rutina para dañar o matar al sprite por salto normal. | ||
Line 173: | Line 275: | ||
=== Interacción Solida === | === Interacción Solida === | ||
La | La hitbox actúa como si fuera un bloque solido con el player. | ||
<div class="toccolours mw-collapsible mw-collapsed" style="width:800px; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}"> | <div class="toccolours mw-collapsible mw-collapsed" style="width:800px; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}"> | ||
Line 379: | Line 481: | ||
</pre> | </pre> | ||
</div> | </div> | ||
<code>'''Nota:''' Para utilizar esta rutina, necesitas activar la opcion de "Process player interaction every frame" en el cfg del sprite</code> | |||
=== Solido por arriba, daña en otras direcciones === | |||
=== Detección === | |||
=== Interacción Spiky === | |||
=== Haciendo el HP === | |||
=== Chequear bordes de la hitbox === | |||
== Interacción con Sprites == | |||
=== Interacción Sprite<->Sprite === | |||
=== Interacción Fireball<->Sprite === | |||
=== Interacción Extended Sprite<->Sprite === | |||
=== Interacción Cluster Sprite<->Sprite === | |||
== ¿Cómo construir la lógica? == | |||
Lo primero que debe ser entendido, es que la lógica del sprite es ejecutada en la rutina <code>SpriteCode</code>: | |||
<pre> | |||
;###################################### | |||
;########## Main Routine ############## | |||
;###################################### | |||
print "MAIN ",pc | |||
PHB | |||
PHK | |||
PLB | |||
JSR SpriteCode | |||
PLB | |||
RTL | |||
;>Routine: SpriteCode | |||
;>Description: This routine excecute the logic of the sprite | |||
;>RoutineLength: Short | |||
Return: | |||
RTS | |||
SpriteCode: | |||
JSR GraphicRoutine ;Calls the graphic routine and updates sprite graphics | |||
;Here you can put code that will be excecuted each frame even if the sprite is locked | |||
LDA !SpriteStatus,x | |||
CMP #$08 ;if sprite dead return | |||
BNE Return | |||
LDA !LockAnimationFlag | |||
BNE Return ;if locked animation return. | |||
JSL SubOffScreen | |||
JSR InteractMarioSprite | |||
;After this routine, if the sprite interact with mario, Carry is Set. | |||
;Here you can write your sprite code routine | |||
;This will be excecuted once per frame excepts when | |||
;the animation is locked or when sprite status is not #$08 | |||
JSR AnimationRoutine ;Calls animation routine and decides the next frame to draw | |||
RTS | |||
</pre> | |||
La parte que vamos a editar sera la que dice: | |||
<pre> | |||
;Here you can write your sprite code routine | |||
;This will be excecuted once per frame excepts when | |||
;the animation is locked or when sprite status is not #$08 | |||
</pre> | |||
Esta zona ocurre siempre que el sprite no este bloqueado y mientras este vivo, además ocurre justo despues de la interacción con el player y antes de ejecutar la rutina de animación. | |||
Luego podemos crear subrutinas debajo del sector que dice: | |||
<pre> | |||
;###################################### | |||
;######## Sub Routine Space ########### | |||
;###################################### | |||
</pre> | |||
Para la lógica del sprite, se recomienda utilizar una maquina de estados, para esto necesitaremos una subrutina llamada <code>StateMachine</code> que es la siguiente: | |||
<pre> | |||
StateMachine: | |||
LDA !State,x | |||
ASL | |||
TAY | |||
REP #$20 | |||
LDA States,y | |||
STA !Scratch0 | |||
SEP #$20 | |||
LDX #$00 | |||
JSR ($0000|!dp,x) | |||
RTS | |||
States: | |||
</pre> | |||
<code>'''Nota:''' Esta rutina permite un máximo de 127 estados distintos.</code> | |||
Esta subrutina ejecuta el estado seleccionado por la variable <code>!State,x</code>, por lo tanto, debemos agregar esta variable en la zona debajo de: | |||
<pre> | |||
;###################################### | |||
;############## Defines ############### | |||
;###################################### | |||
</pre> | |||
Para esto podemos utilizar alguna variable de sprite que sea de tipo miscelanea, por ejemplo: | |||
<pre> | |||
!State = !SpriteMiscTable6 | |||
</pre> | |||
Además en el Init del sprite debemos elegir el estado inicial, asi que agregamos lo siguiente en el Init: | |||
<pre> | |||
LDA #$Estado_Inicial | |||
STA !State,x | |||
</pre> | |||
Una vez se realiza estas acciones, es posible crear estados para el enemigo, por ejemplo, imaginemos que tenemos un enemigo de 2 estados, state1 y state2. Para esto necesitariamos agregarlos a la tabla <code>States</code>: | |||
<pre> | |||
States: | |||
dw state1 | |||
dw state2 | |||
</pre> | |||
En este caso el state1 tendria ID #$00 y state2 tendria ID #$01. | |||
Luego debemos crear los estados de la siguiente manera: | |||
<pre> | |||
state1: | |||
LDX !SpriteIndex | |||
;Here you can write the code of the state | |||
RTS | |||
state2: | |||
LDX !SpriteIndex | |||
;Here you can write the code of the state | |||
RTS | |||
</pre> | |||
Adicionalmente, se recomienda tener una funcion para cuando se cambia entre un estado y otro, para poner las condiciones iniciales de cada estado: | |||
<pre> | |||
state1Start: | |||
;Here you can write the initial conditions of the state | |||
STZ !State,x | |||
RTS | |||
state2Start: | |||
;Here you can write the initial conditions of the state | |||
LDA #$01 | |||
STA !State,x | |||
RTS | |||
</pre> | |||
Entonces, cada vez que se cambie a cierto estado, se llama a la subrutina que cambia a ese estado, por ejemplo, para cambiar al state1: | |||
<pre> | |||
JSR state1Start | |||
</pre> | |||
Eso cambiaría al estado state1 y además pondría todas las condiciones iniciales de ese estado. | |||
Por ultimo, en la zona que dice: | |||
<pre> | |||
;Here you can write your sprite code routine | |||
;This will be excecuted once per frame excepts when | |||
;the animation is locked or when sprite status is not #$08 | |||
</pre> | |||
Agregamos la linea: | |||
<pre> | |||
JSR StateMachine | |||
</pre> | |||
Una vez realizado esto, cuando <code>!State,x</code> sea #$00, se ejecutara el estado <code>state1</code> y cuando sea #$01 se ejecutara el estado <code>state2</code>. | |||
=== ¿Cómo construir un estado? === | |||
Un estado se conforma de las siguientes partes: | |||
* Set up de las condiciones iniciales del estado. | |||
* Comportamiento del sprite mientras esta en ese estado. | |||
* Transiciones a otros estados. | |||
Para realizar el Set up, utilizaremos la rutina que creamos anteriormente que termina con la palabra Start, en esta rutina pondremos todas las cosas que el estado debe tener antes de ejecutarse cada loop, por ejemplo, si el estado fuera Walk, entonces probablemente necesitarías en el Start poner que cambie a la animación de caminar (que asumamos que se llama WalkAnimation): | |||
<pre> | |||
WalkStart: | |||
JSR ChangeAnimationFromStart_WalkAnimation | |||
RTS | |||
</pre> | |||
Entonces cuando cambies al estado Walk utilizando <code>WalkStart</code>, el sprite siempre empezara teniendo la animación de caminar. | |||
Para realizar las acciones, debemos primero pensar en que tipo de acciones queremos realizar en ese estado, por ejemplo, podríamos querer que el sprite camine normalmente, pero que cuando el player este a cierta distancia cambie a otro estado. Por esta razón, dividiremos las acciones realizables en los siguientes grupos: | |||
* Acciones: Son los posibles actos que puede realizar un sprite, por ejemplo, spawnear otro sprite, destruir un bloque, generar algún efecto gráfico, hacer sonar un efecto de sonido, etc. | |||
* Movimientos: Es como se mueve el sprite mientras se ejecuta el sprite, preferí ponerlo distinto a las acciones debido a que el movimiento suele ser continuo y se ejecuta en cada loop, mientras que las acciones se ejecutan en un solo loop. | |||
* Condiciones: Son proposiciones que si se cumplen, se realiza algún tipo de acción o se cambia el movimiento del sprite, por ejemplo, si el player esta a cierta distancia o menos, entonces lanza un proyectil o si el sprite tiene cierta animación y se termino de visualizar cierto frame de la animación entonces se cambia a otra animación. | |||
Generalmente vamos a separar cada comportamiento del sprite en un estado distinto, esto permitirá una mejor manera de organizar el código y de modelarlo mejor. Por ejemplo, si tuviera un sprite como los del Donkey Kong Country (Ejemplo, las abejas), necesitariamos un estado para cuando esta quiero sin hacer nada y un estado para cuando es matado. | |||
Por ultimo, están las transiciones a otros estados. Básicamente, para realizar una transición debemos ver a que estados se pueden llegar desde el estado actual, volviendo al ejemplo del sprite de Donkey Kong Country, la abeja muere por ejemplo cuando le tiran un barril, entonces cuando la abeja detecte interacción con un barril, esta debe transicionar al estado de muerte. Basado en esto debemos identificar los siguientes puntos para hacer transiciones: | |||
* ¿Desde mi estado inicial, a qué estados puedo llegar? | |||
* ¿Qué condiciones se deben cumplir para llegar a esos estados? | |||
Una vez se tiene modelado esto, debemos programar esas condiciones y llamar al Start del estado al que se desea transicionar de la siguiente manera: | |||
<pre> | |||
JSR nextStateStart | |||
</pre> | |||
Ejemplo, digamos que cuando la animación con ID #$01 termine, pasaremos al estado state2, para esto asumiremos, que el frame en la posición #$05 es el ultimo frame de la animación, entonces la transición seria asi: | |||
<pre> | |||
LDA !AnimationIndex,x | |||
CMP #$01 | |||
BNE + | |||
LDA !AnimationFrameIndex,x | |||
CMP #$05 | |||
BCC + | |||
LDA !AnimationTimer,x | |||
BNE + | |||
JSR state2Start | |||
+ | |||
</pre> | |||
Estas transiciones no necesariamente deben ejecutarse desde un estado, también pueden ejecutarse dentro de una acción de interacción, por ejemplo, si cierta hitbox fue tocada por el player, ejecuta su acción y provoca que cambie a otro estado, esto es útil cuando quieres detectar cosas dentro de cierto rango cercano al sprite utilizando el sistema de hitbox. También, se puede utilizar cuando el player salta sobre el sprite y debes pasar a un estado de dañado o de muerte del sprite. | |||
== Rutinas Comunes == | |||
En esta sección se en-listaran varias rutinas que permiten hacer comportamientos para sprites. | |||
=== Condiciones === | |||
==== Chequear paredes, piso o techo ==== | |||
Para chequear necesitas utilizar la dirección de RAM <code>!SpriteBlockedStatus_ASB0UDLR,x</code> o lo que es lo mismo <code>!1588,x</code>, esta dirección tiene formato ASB0UDLR: | |||
* A = El sprite esta tocando el layer 2 por arriba. | |||
* S = El sprite esta tocando el layer 2 por algun lado. | |||
* B = El sprite esta tocando el layer 2 por abajo. | |||
* U = El sprite esta tocando el layer 1 por arriba. | |||
* D = El sprite esta tocando el layer 1 por abajo. | |||
* L = El sprite esta tocando el layer 1 por la izquierda. | |||
* R = El sprite esta tocando el layer 1 por la derecha. | |||
Esta dirección requiere un object clipping adecuado. | |||
Ejemplo de uso: | |||
<pre> | |||
LDA !SpriteBlockedStatus_ASB0UDLR,x | |||
AND #$04 ; Chequearia en este caso el bit D, puedes cambiar este valor para chequear los otros bits | |||
BEQ .NoInteraction | |||
;Acá puedes poner lo que sucede cuando esta tocando el piso | |||
BRA + | |||
.NoInteraction | |||
;Acá puedes poner lo que sucede cuando no esta tocando el piso | |||
+ | |||
</pre> | |||
==== Chequear propiedades de un bloque en una posición especifica ==== | |||
==== Chequear si la animación termino de reproducir un frame determinado ==== | |||
==== Chequear el power up del player ==== | |||
Para chequear el power up actual que esta utilizando el player requieres la siguiente dirección de RAM <code>!PowerUp</code> o lo que es lo mismo <code>$19</code>. | |||
Ejemplo: | |||
<pre> | |||
LDA !PowerUp | |||
CMP #$01 ;en este caso esta chequeando si el player es grande | |||
BCS .IsBig | |||
;Aca ponemos lo que sucederia si es pequeño | |||
BRA + | |||
.IsBig | |||
;Aca ponemos lo que sucederia si es grande | |||
+ | |||
</pre> | |||
==== Chequear el estado actual ==== | |||
==== Chequear el nivel actual ==== | |||
==== ¿Cómo utilizar el extra bit? ==== | |||
==== ¿Cómo utilizar las extra properties? ==== | |||
==== ¿Cómo utilizar los extra bytes? ==== | |||
==== Chequear si el player esta a la derecha, izquierda, arriba o abajo ==== | |||
==== Chequear atributos del cfg ==== | |||
==== Chequear propiedades o variables especificas ==== | |||
=== Acciones === | |||
==== Spawnear Sprites ==== | |||
==== Spawnear Clusters Sprites ==== | |||
==== Spawnear Extended Sprites ==== | |||
==== Spawnear Minor Extended Sprites ==== | |||
==== Spawnear Bounce Sprites ==== | |||
==== Spawnear Smoke Sprites ==== | |||
==== Apuntar ==== | |||
==== Apuntar con gravedad ==== | |||
==== Destruir un bloque ==== | |||
==== Teletransportar al player ==== | |||
==== Finalizar el nivel ==== | |||
==== Ir a otro nivel ==== | |||
==== Modificar velocidad del player ==== | |||
==== Desmontar a Yoshi cuando se selecciona "don't use default interactions" en el cfg ==== | |||
==== Lanzar un mensaje ==== | |||
==== On/Off o Activar Switch ==== | |||
==== Editar Variables ==== | |||
=== Movimientos === | |||
Para el movimiento necesitas las siguientes direcciones de RAM: | |||
* <code>!SpriteXLow,x</code> o <code>!E4,x</code>: Es la posición en X del sprite (Low Byte). | |||
* <code>!SpriteXHigh,x</code> o <code>!14E0,x</code>: Es la posición en X del sprite (High Byte). | |||
* <code>!SpriteYLow,x</code> o <code>!D8,x</code>: Es la posición en Y del sprite (Low Byte). | |||
* <code>!SpriteYHigh,x</code> o <code>!14D4,x</code>: Es la posición en Y del sprite (High Byte). | |||
* <code>!SpriteXSpeed,x</code> o <code>!B6,x</code>: Es la velocidad en X del sprite. | |||
* <code>!SpriteYSpeed,x</code> o <code>!AA,x</code>: Es la velocidad en Y del sprite. | |||
Para utilizar la posición, se recomienda usar direcciones de RAM scratch para tenerlas en 16 bits, Ejemplo: | |||
<pre> | |||
LDA !SpriteXHigh,x | |||
XBA | |||
LDA !SpriteXLow,x | |||
REP #$20 | |||
STA !Scratch0 | |||
SEP #$20 | |||
</pre> | |||
En este caso, la posicion en X, la guardamos en la scratch 0. | |||
La velocidad en X funciona de la siguiente manera: | |||
* Entre #$00 y #$7F, el sprite va hacia la derecha. Mientras más cercano a #$00 más lento es. | |||
* entre #$80 y #$FF, el sprita va hacia la izquierda. Mientras más cercano a #$FF más lento es. | |||
La velocidad en Y funciona de la siguiente manera: | |||
* Entre #$00 y #$7F, el sprite va hacia abajo. Mientras más cercano a #$00 más lento es. | |||
* entre #$80 y #$FF, el sprita va hacia arriba. Mientras más cercano a #$FF más lento es. | |||
Para actualizar la posición, el Super Mario World, tiene 3 rutinas que se llaman con JSL y que la actualizan la posición de acuerdo a la velocidad actual: | |||
* <code>$018022|!rom</code>: Actualiza la posición en X sin considerar gravedad. | |||
* <code>$01801A|!rom</code>: Actualiza la posición en Y sin considerar gravedad. | |||
* <code>$01802A|!rom</code>: Actualiza la posición en X e Y considerando la gravedad e interacción con objetos. | |||
Ejemplo: | |||
<pre> | |||
JSL $01802A|!rom | |||
</pre> | |||
==== Saltar ==== | |||
Para que el sprite salte, debes en un frame especifico, cambiar la velocidad en Y, además requieres chequear si esta en el piso. | |||
<pre> | |||
LDA !SpriteBlockedStatus_ASB0UDLR,x | |||
AND #$04 | |||
BEQ + ;Si no esta tocando el piso entonces no salto | |||
LDA #$E0 ;Puede ser cualquier valor entre #$80 y #$FF | |||
STA !SpriteYSpeed,x ;Seteamos la velocidad | |||
+ | |||
JSL $01802A|!rom ;Actualizamos la velocidad X e Y con gravedad. | |||
</pre> | |||
==== Movimiento acelerado ==== | |||
Para realizar esto, debemos tener una dirección de RAM miscelanea que usemos para la aceleración. Para esto iremos a la zona del codigo que dice lo siguiente: | |||
<pre> | |||
;###################################### | |||
;############## Defines ############### | |||
;###################################### | |||
</pre> | |||
Y creamos un define para la aceleración: | |||
<pre> | |||
!SpriteXAcceleration = !SpriteMiscTable4 ;puede ser cualquier misc, hay 15 en total | |||
</pre> | |||
Luego después de actualizar la posición, debemos actualizar la velocidad, Ejemplo: | |||
<pre> | |||
JSL $01802A|!rom | |||
LDA !SpriteXSpeed,x | |||
CLC | |||
ADC !SpriteXAcceleration,x ;Añadimos la aceleración a la velocidad | |||
STA !SpriteXSpeed,x | |||
</pre> | |||
Si la aceleración esta entre #$80 y #$FF, restara velocidad y si esta entre #$00 y #$7F sumara velocidad | |||
Ahora puedes modificar la aceleración para tener un movimiento acelerado. | |||
==== Movimiento con fricción ==== | |||
==== Físicas en objetos con forma de esfera ==== | |||
==== Físicas en objetos tipo Minecart ==== | |||
==== Controles y movimiento ==== | |||
==== Seguir al jugador ==== | |||
==== Movimiento Wave ==== | |||
==== Mover al jugador junto con el sprite (Útil para Sprites Solidos) ==== | |||
==== Rebotar ==== | |||
==== Movimiento en forma de Seno o Coseno ==== | |||
==== Movimiento en forma de Ellipse o Circulo ==== | |||
==== Movimiento en forma de Cardiode ==== | |||
==== Movimiento Espiral ==== | |||
==== Line Guided ==== | |||
==== Terrain Guided ==== | |||
==== ¿Cómo construir movimientos usando funciones y derivadas? ==== |
Latest revision as of 17:33, 3 June 2019
Dyzen Sprite Maker es un tool creado por Anonimzwx perteneciente a SNES Office que facilita la creación de enemigos (o cualquier objeto que use tiles de la OAM), originalmente fue hecho para el videojuego Super Mario World, pero hay planes para que funcione con otros videojuegos.
Las funcionalidades de esta herramienta son las siguientes:
- Creación de la rutina gráfica.
- Creación de animaciones.
- Creación de cajas de colisión.
- Generador automático de código ASM con editor.
Video-Tutoriales
Por el momento se encuentran los siguientes tutoriales:
Colaboración
El Github de Dyzen es el siguiente:
https://github.com/weierstrass1/SMWControlib
Cualquier duda se puede comunicar con Anonimzwx.
Actualmente se busca gente que pueda documentar el sistema de enemigos otros videojuegos para darles soporte
Variables
Para comprender como funciona el sistema de animaciones debemos comprender el como esta modelado. Para esto debemos separarlo en 2 partes:
Frames
Un sprite generado por Dyzen, contiene un conjunto de frames. Cada frame posee sus propias hitboxes, de esta manera al elegir un frame, se selecciona la interacción que posee. Además cada frame tiene un ID, que sirve para poder seleccionarlo. El frame seleccionado es el que es mostrado por el sprite.
Para seleccionar un frame, utilizamos la variable !FrameIndex,x
, si le ponemos ID del frame que deseamos, el sprite automáticamente mostrara ese ID, aquí un ejemplo de como hacerlo:
LDA #$ID_Del_Frame_Deseado STA !FrameIndex,x
Si utilizas el sistema de animaciones generados por el tool, entonces no necesitas cambiar esta variable normalmente.
El frame mostrado también puede ser volteado horizontal o verticalmente, para esto utilizamos la variable !GlobalFlip,x
, los valores que admite son:
- #$00 = Sin volteo
- #$01 = Volteo horizontal
- #$02 = Volteo vertical
- #$03 = Volteo horizontal y vertical.
Un ejemplo de uso es:
LDA #$01 STA !GlobalFlip,x
Esto voltearia al sprite horizontalmente.
Animaciones
Una animación es un subconjunto de frames del conjunto total que contiene el sprite. Esta animación recorre sus frames desde el primero hasta el ultimo, además existen las animaciones que se reproducen solo una vez (Only Once) y las que se reproducen de forma continua (Continuous). Cada animación tiene su propio ID.
Para regular el tiempo entre cada frame de la animación, se utiliza la variable !AnimationTimer,x
, esta variable indica la cantidad de loops para pasar al siguiente frame, por ejemplo, si el valor de esta variable fuera 2, entonces en 2 loops el frame cambiara al siguiente.
Nota: Cada loop dura 1/60 segundos.
Para regular el frame actual que es mostrado por la animación, utilizamos la variable !AnimationFrameIndex,x
, esta variable contiene la posición del frame que es reproducido actualmente, las posiciones inician desde 0 . Por ejemplo, si la animación tuviera 4 frames y esta variable tuviera el valor 1, entonces, el frame mostrado seria el segundo.
Para regular si el frame actual debe ser volteado, utilizamos !LocalFlip,x
, esta variable es similar a !GlobalFlip,x
, solo que esta solo afecta al frame actual que se reproduce, al cambiar de frame tomara otro valor, la utilidad de esta variable es que permite utilizar frames volteados dentro de la animación, que a su vez pueden ser volteados por el !GlobalFlip,x
, para efectos practicos, en situaciones sumamente excepcionales requeriras utilizarla, asi que puedes ignorarla.
Para seleccionar una animación, debemos utilizar la variable !AnimationIndex,x
. Debemos tomar en cuenta que cuando cambiamos esta variable, debemos cambiar también !AnimationFrameIndex,x
, !AnimationTimer,x
y !FrameIndex,x
(incluso en algunos casos el !LocalFlip,x
). Por esta razón, el tool genera rutinas para iniciar cada animación desde el inicio que pueden ser llamadas, estas rutinas son las que tienen nombre del estilo ChangeAnimationFromStart_<animation_name>
, por ejemplo, si tengo una animacion que se llama "Animation1", la rutina que permite empezar esa animación desde el inicio es ChangeAnimationFromStart_Animation1
. Podemos llamar estas rutinas, para cambiar la animación y que esta se reproduzca desde el primer frame de su lista de la siguiente manera:
JSR ChangeAnimationFromStart_Animation1
Además, el tool genera una rutina llamada InitWrapperChangeAnimationFromStart la que requiere ser llamada en el Init del sprite. Esta rutina da la instrucción de hacer que el sprite comience con la animación de ID 0 cuando el sprite sea creado, si deseas iniciar desde otra animación puedes reemplazar la linea que dice:
STZ !AnimationIndex,x
Por:
LDA #$ID_De_La_Animacion_Deseada STA !AnimationIndex,x
Si de todas maneras, no quieres usar estos métodos, puedes siempre hacer el tuyo propio, para esto solo debes utilizar las variables antes mencionadas, por ejemplo, si deseas que la animación #$7 inicie desde el frame en la posición #$5 (que a su vez su ID es el #$10), que el primer frame tarde #$03 loops y que inicie volteado horizontalmente, puedes hacer lo siguiente:
LDA #$07 STA !AnimationIndex,x ;Set the animation Index LDA #$05 STA !AnimationFrameIndex,x ;Set the current frame of the animation to show LDA #$10 STA !FrameIndex,x ;Set the ID of the frame to show LDA #$03 STA !AnimationTimer,x ;Set the number of loops that will be showed the frame LDA #$01 STA !LocalFlip,x ;Flip the frame
También puedes chequear que el cambio de animación se haga justo cuando termina cierto frame de otra animación:
ChangeToAnotherAnimation: LDA !AnimationIndex,x CMP #$ID_De_La_Animacion_Que_Se_Quiere_Cambiar BNE + LDA !AnimationFrameIndex,x CMP #$Posicion_Del_Frame BNE + LDA !AnimationTimer,x BNE + JSR ChangeAnimationFromStart_Name_Of_The_New_Animation + RTS
Acciones para Hitbox
Las acciones de las hitboxes son pequeños trozos de código que son ejecutados cuando cierta hitbox colisiona con el player. De momento solo funcionan con el player, a futuro se implementaran para interacción sprite<->sprite.
Nota: Todas las siguientes acciones funcionan solo en el videojuego Super Mario World.
Antes que todo debemos considerar que el tool permite tener multiples hitboxes, debido a esto existen 2 tipos de acciones:
La acción default es la que ejecutaria la mayoria de las cajas de colisión y que es autogenerada por el tool y que se veria asi:
DefaultAction: ;Here write the code RTS
Nota: Existe un bug en que si editas la acción default desde el editor de código y vuelves a recargar el codigo, volvera a su estado inicial, por lo tanto, se recomienda editar esta en el archivo generado al hacer "Extract Resources" y no en el editor de codigo del tool.
Y las acciónes no default, que son creadas en el apartado de interacción por el usuario, por ejemplo:
notDefaultAction: LDX !SpriteIndex ;Here write the code. RTS
A continuación algunos códigos de ejemplo que pueden utilizarse, estos deben ser puestos en la zona que dice ;Here you can write your action code
:
Dañar al player
Daña al jugador cuando toca la hitbox.
JSL $00F5B7|!rom
Matar al player
Mata al jugador cuando toca la hitbox.
JSL $00F606|!rom
Interacción Default
Si el player salta sobre la hitbox, es impulsado para arriba, en otro caso es dañado.
Puedes reemplazar ;Here you can call a routine to damage the sprite by normal jump
por alguna rutina para dañar o matar al sprite por salto normal.
Puedes reemplazar ;Here you can call a routine to damage the sprite by spin jump.
por alguna rutina para dañar o matar al sprite por spin jump.
LDA $1490|!addr BEQ + JSL StarKill ;If mario have star then kill the sprite using the STAR, it use GIEPY RTS + LDA !PlayerYSpeed BPL +++ ;If Player Y Speed is Negative then damage player JSL $00F5B7|!rom RTS +++ STZ !ScratchD LDA !Scratch3 STA !ScratchC LDA !Scratch9 XBA LDA !Scratch1 REP #$20 CLC ADC !ScratchC SEC SBC #$0008 STA !ScratchC ;Obtain the position of the lowest 8 pixels of mario SEP #$20 LDA !ScratchB XBA LDA !Scratch5 REP #$20 CMP !ScratchC SEP #$20 ;If the interaction happend using only the lowest 8 pixels of the player BCS +++ ;And player Y speed is positive then start normal kill JSL $00F5B7|!rom RTS +++ JSL $01AB99|!rom ;Spawn a White Star bellow mario JSL $01AA33|!rom ;Boost Player Y Speed LDA $140D|!addr BEQ + ;Here you can call a routine to damage the sprite by spin jump. RTS + JSR JumpSound ;Here you can call a routine to damage the sprite by normal jump RTS JumpSound: PHY LDA $1697|!addr CLC ADC !SpriteMiscTable13,x INC $1697|!addr TAY INY CPY.B #$08 BCS + LDA Return01A61D,y STA $1DF9|!addr ; / Play sound effect + TYA CMP #$08 BCC + LDA #$08 + JSL $02ACE5|!rom PLY RTS ; Return Return01A61D: RTS ; Return DATA_01A61E: db $13,$14,$15,$16,$17,$18,$19
Interacción Solida
La hitbox actúa como si fuera un bloque solido con el player.
LDA !PlayerYSpeed STA !ScratchF STZ !ScratchE LDA !PlayerYSpeed BMI +++ STZ !ScratchD LDA !Scratch3 STA !ScratchC LDA !Scratch9 XBA LDA !Scratch1 REP #$20 CLC ADC !ScratchC SEC SBC #$0008 STA !ScratchC SEP #$20 LDA !ScratchB XBA LDA !Scratch5 REP #$20 CMP !ScratchC BCS + JMP .sides + PHA LDA !ScratchC SEC SBC $01,s ADC #$0008 STA !ScratchC PLA LDA !PlayerY SEC SBC !ScratchC STA !PlayerY SEP #$20 LDA !PlayerBlockedStatus_S00MUDLR ORA #$04 STA !PlayerBlockedStatus_S00MUDLR STZ !PlayerInAirFlag LDA #$01 STA $1471|!base2 RTS +++ SEP #$20 STZ !ScratchD LDA !Scratch7 STA !ScratchC LDA !ScratchB XBA LDA !Scratch5 REP #$20 CLC ADC !ScratchC SEC SBC #$0008 STA !ScratchC SEP #$20 LDA !Scratch9 XBA LDA !Scratch1 REP #$20 CMP !ScratchC BCC +++ SEP #$20 LDA #$08 STA !PlayerYSpeed LDA #$01 STA !ScratchE LDA !PlayerBlockedStatus_S00MUDLR ORA #$08 STA !PlayerBlockedStatus_S00MUDLR +++ .sides SEP #$20 LDA !PlayerXSpeed BMI +++ STZ !ScratchD LDA !Scratch2 STA !ScratchC LDA !Scratch8 XBA LDA !Scratch0 REP #$20 CLC ADC !ScratchC SEC SBC #$0008 STA !ScratchC SEP #$20 LDA !ScratchA XBA LDA !Scratch4 REP #$20 CMP !ScratchC BCC ++ PHA LDA !ScratchC SEC SBC $01,s ADC #$0008 STA !ScratchC PLA LDA !PlayerX SEC SBC !ScratchC STA !PlayerX SEP #$20 STZ !PlayerXSpeed LDA !ScratchE BEQ + LDA !ScratchF STA !PlayerYSpeed LDA !PlayerBlockedStatus_S00MUDLR AND #$F7 STA !PlayerBlockedStatus_S00MUDLR + LDA !PlayerBlockedStatus_S00MUDLR ORA #$01 STA !PlayerBlockedStatus_S00MUDLR RTS ++ SEP #$20 LDA !PlayerXSpeed BEQ +++ RTS +++ SEP #$20 STZ !ScratchD LDA !Scratch6 STA !ScratchC LDA !ScratchA XBA LDA !Scratch4 REP #$20 CLC ADC !ScratchC SEC SBC #$0008 STA !ScratchC SEP #$20 LDA !Scratch8 XBA LDA !Scratch0 REP #$20 CMP !ScratchC BCC +++ PHA LDA !ScratchC SEC SBC $01,s ADC #$0008 STA !ScratchC PLA LDA !PlayerX CLC ADC !ScratchC STA !PlayerX SEP #$20 STZ !PlayerXSpeed LDA !ScratchE BEQ + LDA !ScratchF STA !PlayerYSpeed LDA !PlayerBlockedStatus_S00MUDLR AND #$F7 STA !PlayerBlockedStatus_S00MUDLR + LDA !PlayerBlockedStatus_S00MUDLR ORA #$02 STA !PlayerBlockedStatus_S00MUDLR RTS +++ SEP #$20
Nota: Para utilizar esta rutina, necesitas activar la opcion de "Process player interaction every frame" en el cfg del sprite
Solido por arriba, daña en otras direcciones
Detección
Interacción Spiky
Haciendo el HP
Chequear bordes de la hitbox
Interacción con Sprites
Interacción Sprite<->Sprite
Interacción Fireball<->Sprite
Interacción Extended Sprite<->Sprite
Interacción Cluster Sprite<->Sprite
¿Cómo construir la lógica?
Lo primero que debe ser entendido, es que la lógica del sprite es ejecutada en la rutina SpriteCode
:
;###################################### ;########## Main Routine ############## ;###################################### print "MAIN ",pc PHB PHK PLB JSR SpriteCode PLB RTL ;>Routine: SpriteCode ;>Description: This routine excecute the logic of the sprite ;>RoutineLength: Short Return: RTS SpriteCode: JSR GraphicRoutine ;Calls the graphic routine and updates sprite graphics ;Here you can put code that will be excecuted each frame even if the sprite is locked LDA !SpriteStatus,x CMP #$08 ;if sprite dead return BNE Return LDA !LockAnimationFlag BNE Return ;if locked animation return. JSL SubOffScreen JSR InteractMarioSprite ;After this routine, if the sprite interact with mario, Carry is Set. ;Here you can write your sprite code routine ;This will be excecuted once per frame excepts when ;the animation is locked or when sprite status is not #$08 JSR AnimationRoutine ;Calls animation routine and decides the next frame to draw RTS
La parte que vamos a editar sera la que dice:
;Here you can write your sprite code routine ;This will be excecuted once per frame excepts when ;the animation is locked or when sprite status is not #$08
Esta zona ocurre siempre que el sprite no este bloqueado y mientras este vivo, además ocurre justo despues de la interacción con el player y antes de ejecutar la rutina de animación.
Luego podemos crear subrutinas debajo del sector que dice:
;###################################### ;######## Sub Routine Space ########### ;######################################
Para la lógica del sprite, se recomienda utilizar una maquina de estados, para esto necesitaremos una subrutina llamada StateMachine
que es la siguiente:
StateMachine: LDA !State,x ASL TAY REP #$20 LDA States,y STA !Scratch0 SEP #$20 LDX #$00 JSR ($0000|!dp,x) RTS States:
Nota: Esta rutina permite un máximo de 127 estados distintos.
Esta subrutina ejecuta el estado seleccionado por la variable !State,x
, por lo tanto, debemos agregar esta variable en la zona debajo de:
;###################################### ;############## Defines ############### ;######################################
Para esto podemos utilizar alguna variable de sprite que sea de tipo miscelanea, por ejemplo:
!State = !SpriteMiscTable6
Además en el Init del sprite debemos elegir el estado inicial, asi que agregamos lo siguiente en el Init:
LDA #$Estado_Inicial STA !State,x
Una vez se realiza estas acciones, es posible crear estados para el enemigo, por ejemplo, imaginemos que tenemos un enemigo de 2 estados, state1 y state2. Para esto necesitariamos agregarlos a la tabla States
:
States: dw state1 dw state2
En este caso el state1 tendria ID #$00 y state2 tendria ID #$01.
Luego debemos crear los estados de la siguiente manera:
state1: LDX !SpriteIndex ;Here you can write the code of the state RTS state2: LDX !SpriteIndex ;Here you can write the code of the state RTS
Adicionalmente, se recomienda tener una funcion para cuando se cambia entre un estado y otro, para poner las condiciones iniciales de cada estado:
state1Start: ;Here you can write the initial conditions of the state STZ !State,x RTS state2Start: ;Here you can write the initial conditions of the state LDA #$01 STA !State,x RTS
Entonces, cada vez que se cambie a cierto estado, se llama a la subrutina que cambia a ese estado, por ejemplo, para cambiar al state1:
JSR state1Start
Eso cambiaría al estado state1 y además pondría todas las condiciones iniciales de ese estado.
Por ultimo, en la zona que dice:
;Here you can write your sprite code routine ;This will be excecuted once per frame excepts when ;the animation is locked or when sprite status is not #$08
Agregamos la linea:
JSR StateMachine
Una vez realizado esto, cuando !State,x
sea #$00, se ejecutara el estado state1
y cuando sea #$01 se ejecutara el estado state2
.
¿Cómo construir un estado?
Un estado se conforma de las siguientes partes:
- Set up de las condiciones iniciales del estado.
- Comportamiento del sprite mientras esta en ese estado.
- Transiciones a otros estados.
Para realizar el Set up, utilizaremos la rutina que creamos anteriormente que termina con la palabra Start, en esta rutina pondremos todas las cosas que el estado debe tener antes de ejecutarse cada loop, por ejemplo, si el estado fuera Walk, entonces probablemente necesitarías en el Start poner que cambie a la animación de caminar (que asumamos que se llama WalkAnimation):
WalkStart: JSR ChangeAnimationFromStart_WalkAnimation RTS
Entonces cuando cambies al estado Walk utilizando WalkStart
, el sprite siempre empezara teniendo la animación de caminar.
Para realizar las acciones, debemos primero pensar en que tipo de acciones queremos realizar en ese estado, por ejemplo, podríamos querer que el sprite camine normalmente, pero que cuando el player este a cierta distancia cambie a otro estado. Por esta razón, dividiremos las acciones realizables en los siguientes grupos:
- Acciones: Son los posibles actos que puede realizar un sprite, por ejemplo, spawnear otro sprite, destruir un bloque, generar algún efecto gráfico, hacer sonar un efecto de sonido, etc.
- Movimientos: Es como se mueve el sprite mientras se ejecuta el sprite, preferí ponerlo distinto a las acciones debido a que el movimiento suele ser continuo y se ejecuta en cada loop, mientras que las acciones se ejecutan en un solo loop.
- Condiciones: Son proposiciones que si se cumplen, se realiza algún tipo de acción o se cambia el movimiento del sprite, por ejemplo, si el player esta a cierta distancia o menos, entonces lanza un proyectil o si el sprite tiene cierta animación y se termino de visualizar cierto frame de la animación entonces se cambia a otra animación.
Generalmente vamos a separar cada comportamiento del sprite en un estado distinto, esto permitirá una mejor manera de organizar el código y de modelarlo mejor. Por ejemplo, si tuviera un sprite como los del Donkey Kong Country (Ejemplo, las abejas), necesitariamos un estado para cuando esta quiero sin hacer nada y un estado para cuando es matado.
Por ultimo, están las transiciones a otros estados. Básicamente, para realizar una transición debemos ver a que estados se pueden llegar desde el estado actual, volviendo al ejemplo del sprite de Donkey Kong Country, la abeja muere por ejemplo cuando le tiran un barril, entonces cuando la abeja detecte interacción con un barril, esta debe transicionar al estado de muerte. Basado en esto debemos identificar los siguientes puntos para hacer transiciones:
- ¿Desde mi estado inicial, a qué estados puedo llegar?
- ¿Qué condiciones se deben cumplir para llegar a esos estados?
Una vez se tiene modelado esto, debemos programar esas condiciones y llamar al Start del estado al que se desea transicionar de la siguiente manera:
JSR nextStateStart
Ejemplo, digamos que cuando la animación con ID #$01 termine, pasaremos al estado state2, para esto asumiremos, que el frame en la posición #$05 es el ultimo frame de la animación, entonces la transición seria asi:
LDA !AnimationIndex,x CMP #$01 BNE + LDA !AnimationFrameIndex,x CMP #$05 BCC + LDA !AnimationTimer,x BNE + JSR state2Start +
Estas transiciones no necesariamente deben ejecutarse desde un estado, también pueden ejecutarse dentro de una acción de interacción, por ejemplo, si cierta hitbox fue tocada por el player, ejecuta su acción y provoca que cambie a otro estado, esto es útil cuando quieres detectar cosas dentro de cierto rango cercano al sprite utilizando el sistema de hitbox. También, se puede utilizar cuando el player salta sobre el sprite y debes pasar a un estado de dañado o de muerte del sprite.
Rutinas Comunes
En esta sección se en-listaran varias rutinas que permiten hacer comportamientos para sprites.
Condiciones
Chequear paredes, piso o techo
Para chequear necesitas utilizar la dirección de RAM !SpriteBlockedStatus_ASB0UDLR,x
o lo que es lo mismo !1588,x
, esta dirección tiene formato ASB0UDLR:
- A = El sprite esta tocando el layer 2 por arriba.
- S = El sprite esta tocando el layer 2 por algun lado.
- B = El sprite esta tocando el layer 2 por abajo.
- U = El sprite esta tocando el layer 1 por arriba.
- D = El sprite esta tocando el layer 1 por abajo.
- L = El sprite esta tocando el layer 1 por la izquierda.
- R = El sprite esta tocando el layer 1 por la derecha.
Esta dirección requiere un object clipping adecuado.
Ejemplo de uso:
LDA !SpriteBlockedStatus_ASB0UDLR,x AND #$04 ; Chequearia en este caso el bit D, puedes cambiar este valor para chequear los otros bits BEQ .NoInteraction ;Acá puedes poner lo que sucede cuando esta tocando el piso BRA + .NoInteraction ;Acá puedes poner lo que sucede cuando no esta tocando el piso +
Chequear propiedades de un bloque en una posición especifica
Chequear si la animación termino de reproducir un frame determinado
Chequear el power up del player
Para chequear el power up actual que esta utilizando el player requieres la siguiente dirección de RAM !PowerUp
o lo que es lo mismo $19
.
Ejemplo:
LDA !PowerUp CMP #$01 ;en este caso esta chequeando si el player es grande BCS .IsBig ;Aca ponemos lo que sucederia si es pequeño BRA + .IsBig ;Aca ponemos lo que sucederia si es grande +
Chequear el estado actual
Chequear el nivel actual
¿Cómo utilizar el extra bit?
¿Cómo utilizar las extra properties?
¿Cómo utilizar los extra bytes?
Chequear si el player esta a la derecha, izquierda, arriba o abajo
Chequear atributos del cfg
Chequear propiedades o variables especificas
Acciones
Spawnear Sprites
Spawnear Clusters Sprites
Spawnear Extended Sprites
Spawnear Minor Extended Sprites
Spawnear Bounce Sprites
Spawnear Smoke Sprites
Apuntar
Apuntar con gravedad
Destruir un bloque
Teletransportar al player
Finalizar el nivel
Ir a otro nivel
Modificar velocidad del player
Desmontar a Yoshi cuando se selecciona "don't use default interactions" en el cfg
Lanzar un mensaje
On/Off o Activar Switch
Editar Variables
Movimientos
Para el movimiento necesitas las siguientes direcciones de RAM:
!SpriteXLow,x
o!E4,x
: Es la posición en X del sprite (Low Byte).!SpriteXHigh,x
o!14E0,x
: Es la posición en X del sprite (High Byte).!SpriteYLow,x
o!D8,x
: Es la posición en Y del sprite (Low Byte).!SpriteYHigh,x
o!14D4,x
: Es la posición en Y del sprite (High Byte).!SpriteXSpeed,x
o!B6,x
: Es la velocidad en X del sprite.!SpriteYSpeed,x
o!AA,x
: Es la velocidad en Y del sprite.
Para utilizar la posición, se recomienda usar direcciones de RAM scratch para tenerlas en 16 bits, Ejemplo:
LDA !SpriteXHigh,x XBA LDA !SpriteXLow,x REP #$20 STA !Scratch0 SEP #$20
En este caso, la posicion en X, la guardamos en la scratch 0.
La velocidad en X funciona de la siguiente manera:
- Entre #$00 y #$7F, el sprite va hacia la derecha. Mientras más cercano a #$00 más lento es.
- entre #$80 y #$FF, el sprita va hacia la izquierda. Mientras más cercano a #$FF más lento es.
La velocidad en Y funciona de la siguiente manera:
- Entre #$00 y #$7F, el sprite va hacia abajo. Mientras más cercano a #$00 más lento es.
- entre #$80 y #$FF, el sprita va hacia arriba. Mientras más cercano a #$FF más lento es.
Para actualizar la posición, el Super Mario World, tiene 3 rutinas que se llaman con JSL y que la actualizan la posición de acuerdo a la velocidad actual:
$018022|!rom
: Actualiza la posición en X sin considerar gravedad.$01801A|!rom
: Actualiza la posición en Y sin considerar gravedad.$01802A|!rom
: Actualiza la posición en X e Y considerando la gravedad e interacción con objetos.
Ejemplo:
JSL $01802A|!rom
Saltar
Para que el sprite salte, debes en un frame especifico, cambiar la velocidad en Y, además requieres chequear si esta en el piso.
LDA !SpriteBlockedStatus_ASB0UDLR,x AND #$04 BEQ + ;Si no esta tocando el piso entonces no salto LDA #$E0 ;Puede ser cualquier valor entre #$80 y #$FF STA !SpriteYSpeed,x ;Seteamos la velocidad + JSL $01802A|!rom ;Actualizamos la velocidad X e Y con gravedad.
Movimiento acelerado
Para realizar esto, debemos tener una dirección de RAM miscelanea que usemos para la aceleración. Para esto iremos a la zona del codigo que dice lo siguiente:
;###################################### ;############## Defines ############### ;######################################
Y creamos un define para la aceleración:
!SpriteXAcceleration = !SpriteMiscTable4 ;puede ser cualquier misc, hay 15 en total
Luego después de actualizar la posición, debemos actualizar la velocidad, Ejemplo:
JSL $01802A|!rom LDA !SpriteXSpeed,x CLC ADC !SpriteXAcceleration,x ;Añadimos la aceleración a la velocidad STA !SpriteXSpeed,x
Si la aceleración esta entre #$80 y #$FF, restara velocidad y si esta entre #$00 y #$7F sumara velocidad
Ahora puedes modificar la aceleración para tener un movimiento acelerado.