Animacion Sprites

Retomamos el codigo para esta vez comenzar a animar nuestro personaje… Hoy empiezo mostrando todo lo que hice en un video, y despues vamos con el codigo y las complicaciones, de paso vemos un poco de teoria que tuve que aprender.

Como podemos ver, ahora nuestro personaje hace bastante mas cosas. En principio el grafico ‘gira’ para el lado que se mueve, lo que obligó a modificar la rutina de disparo para que, segun para donde este mirando nuestro personaje, salga el laser.
Ademas hay gravedad, o algo parecido… ahora si pulsamos para arriba nos ‘impulsamos’, y si soltamos la gravedad nos tira para abajo hasta tocar el piso.
Como nuestro personaje no puede caminar… bah, no tengo ganas de hacer tooooda la animacion de las patitas… lo resolvi muuuy simple: si estas tocando el piso NO TE PODES MOVER.
La idea detras de esto es la siguiente:

  • Solo te podes mover si volas con el jetpac
  • Solo podes volar si tenes combustible
  • Si te quedas sin combustible quedas anclado en el piso (y seras masacrado horriblemente por tu competidor)
  • Podes recargar mas combustible con celdas de combustible que cae del cielo (y podes dispararle a las celdas para evitar que tu competidor agarre combustible)
  • Hablando de disparos: cada tiro que disparas te quita una unidad de energia (para no ponerte a disparar como loco)
  • Cada tiro que te ponen te resta 30 unidades de energia
  • Cada vez que volas perdes energia (no tanto como si te pusieran un tiro, pero perdes)… todavia a definir

O sea: perdes energia por cualquier cosa, y tenes que pelear por recuperarla.
De todo eso solo tengo implementado el primer punto, los restantes los ire implementando mas adelante.

Ahora ¡El codigo!

Lo primero que explicaré es como hice la animacion del jetpac (que mire para donde se está moviendo). Es super sencillo, abrí el archivo sprites.asm y definí un grafico identico al que habia realizado previamente, pero lo puse en espejo. Lo puse un label con nombre jetpacLeft, a continuacion de la definicion del disparo. Codigo a continuación:

jetpacLeft
          bits           ..........BBBB..........
          bits           ............BBB.........
          bits           ........BBBB.BB.B.......
          bits           ........BBBB.BB.B.......
          bits           ........BBBB.BB.B.......
          bits           .........BBBBB..BB......    
          bits           ................BB......
          bits           ..........BBBB.B.B......
          bits           .......B.BB..B.BBB......
          bits           .......B.BB..B.B.B......
          bits           ....BBBBB....B.BBB......
          bits           .........BBBB.BB.B......
          bits           ..........BBB.BBBB......
          bits           ......BBBBBB.BBBB.......
          bits           ......BBBBBB..B.B.......
          bits           ......BB.....B.B.B......
          bits           .............BBBBB......
          bits           .....BBB................
          bits           ....BBBB.....B.BB.......
          bits           .............B..B.......
          bits           ...............B..B.....
          byte           0

Luego agregue unas variables en vars.asm (en realidad las uso como constantes, pero bue… todo lo que definimos aca son punteros o constantes):

; constantes
gravity   = $05   ; usado para setear la 'fuerza' de la gravedad, 1 = super fuerte, mayor = mas debil
floorPosition = 223
topPosition = 52

ptrJPLeft = $23
ptrJPRight = $21
ptrJPFire = $22
; dirFire esta en la direccion de memoria que me sobra del sprite de disparo... mhmm          

ademas de una de nombre dirFire, que es el ultimo byte del sprite ptrJPRight, asi lo aprovechamos para algo (puse ese comentario con las demas variables porque me paso que no me acordaba donde la habia definido… si… es medio una cochinada, pero solo tengo 65535 espacios libres de memoria… y ni siquiera eso).
Las variables ptrJPLeft y ptrJPRight guardan el puntero de la ubicacion de los sprites. Podria haber utilizado los numeros, pero el codigo queda mucho mas claro asi. Estas vars/const (llamenlas como quieran) las utilizo en animatePlayer1.asm. A continuacion un codigo de ejemplo (no es el codigo exactamente, borre un pedacito para clarificar):

chkLeft   lda            joy2      
          and            #4         ; checkeo si es left
          bne            chkRight   ; si no voy a checkear right
                                    ; y si es left, sigo por aca
          lda            #ptrJPLeft ; cargo en a el valor de ptrJPLeft ($23)     
          sta            sprpoint   ; y lo establezco en el puntero del jetpac
          dec            sprx       ; y todo lo demas... .

Lo mismo para right, o sea, en cada cambio derecha/izquierda lo unico que hago es cambiar un numerito en el puntero del sprite del jetpac1. De la misma forma, si quisieramos hacer que camine, o que expulse fuego por el jetpac cuando vuela lo que deberemos hacer es armar un contador, que se incremente cada n frames, que segun el numero que establezca el puntero del frame correspondiente… es laborioso, pero no imposible.

Un detalle que tuve que cambiar fue el tema de los disparos. Antes siempre miraba hacia la derecha, por lo que la rutina de disparos era tan elemental como que cuando detectaba el boton de disparo procedia a:

  • tomar la posicion x e y del jugador, establecia la posicion x e y del disparo, y pasaba el status a disparando.
  • mientras estaba disparando en cada frame avanzaba 3 pixeles, y chequeaba que no haya llegado al final de la pantalla (si x es MAYOR o IGUAL que una determinada posicion – esto tiene profundas implicancias mas adelante).
  • si llegaba al final de la pantalla procedia a apagar el sprite de disparo, y pasaba a estado esperando.

Ahora tengo que tener en cuenta ademas para adonde esta mirando, e inicializar una variable dirFire para mas adelante, cuando este en status disparando, decremente o incremente la posicion del disparo.

Anteriormente dije que chequeaba si el disparo habia finalizado cuando llegaba al final de la pantalla, o sea a una posicion x MAYOR o IGUAL a un punto arbitrario. En assembler no es tan trivial como en cualquier otro lenguaje hacer una comparacion por mayor o igual (hay que hacer una operacion, y ver si ademas tenemos acarreo). Eso se hace cargando en A la posicion x del sprite, luego hacemos un ADC, que es una operacion para sumar, pero con acarreo, ponemos el resultado de la suma en la posicion x del sprite, y con BCS (Branch on Carry Set) saltamos a status0 si se produjo un acarreo (o sea la suma excedio 255). Codigo de ejemplo donde hago eso:

        lda            sprxFire  
        clc             ; seteamos el flag de acarreo en 0
        adc            #$02 ; cantidad de pixels que aumenta
        sta            sprxFire  
        bcs            @setStatus0        

Diferente es cuando en vez de sumar tenemos que restar. Hacemos exactamente lo mismo, pero con unas salvedades:

  • en vez de ADC usamos SBC, que a diferencia de ADC, BORRA el flag de acarreo cuando se excede el valor de una resta (o sea, da menos que cero). Eso nos lleva a …
  • en vez de CLC usamos SEC, que ESTABLECE el flag de acarreo, para finalmente chequear con
  • BCC (Branch on Carry Clear), lo mismo que el otro, pero cuando borra el Carry.

esta boludez me tuvo una semana, todo por no leer la puta documentacion. Quedais avisaos…

Otro detalle que agregue fue la gravedad y los limites superiores e inferiores. Para la gravedad agregue dos constantes gravity, que es el valor inicial del contador, y gravityCounter, que es el contador de gravedad. Al final de cada frame decremento gravityCounter y me fijo llego a cero. Si no llego a cero decremento el contador en 1, y si es igual a cero decremento la posicion Y del sprite e inicializo el gravityCounter con el valor de gravity.

next
          ldx            gravityCounter
          dex
          stx            gravityCounter
          cpx            #$0       
          bne            @exit

          jsr            checkFloorP1

          cpx            #1      ; check floor (si result checkFloorP1 es 1 es el piso)
          beq            @skipGravity

          inc            spry      ; gravity :P 

@skipGravity
          ldx            #gravity
          stx            gravityCounter

Pueden ver ademas que hago una llamada a una subrutina de nombre checkFloorP1. Esta rutina checkea que si el jugador llego al piso, y establece el registro x con 1 o 0, comparamos x con 1 o 0 (true o false, seria) y procedemos… a continuacion la rutina para checkear si llego al piso (y de paso la rutina para chequear el techo, de regalo).

; verifica posicion Y de P1, y retorna 1 en X si es el piso 
checkFloorP1
          ldx            spry      
          cpx            #floorPosition
          beq            @returnTrue    ; is equal
          bcs            @returnTrue    ; or greater ;) 
          ldx            #0        
          rts
@returnTrue
          ldx            #1        
          rts

; verifica posicion Y de P1, y retorna 1 en X si es el top
checkTopP1
          ldx            spry      
          cpx            #topPosition
          beq            @returnTrue    ; is equal
          ;bcs            @returnTrue    ; or greater ;) 
          ldx            #0        
          rts
@returnTrue
          ldx            #1        
          rts

A partir de ahora no voy a poner tanto codigo ya que ademas de codigo nuevo voy corrigiendo el codigo que escribi previamente, o hago alguna modificacion de ultimo momento, asi que prefiero explicar lo que hice, tratando de entenderlo uds y yo, y pueden ir bajando desde Github lo que tengo realizado hasta el momento:
https://github.com/moonorongo/jp_wars.git

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s