Sprites III: Transparencia (C y ASM con SDCC)

Click here to see in English

En este tutorial vamos a ver como pintar un sprite con transparencia sobre un fondo, para que encaje perfectamente. Para ello vamos a utilizar los graficos del arcade Bomberman (Dynablaster).Vamos a usar un fondo del juego y el personaje principal (ya retocado):

dyna

En grande:

dyna

y el fondo del juego:

dyna

Como puede verse en el sprite del personaje principal, el rosa es el color que pretendemos que no se pinte (color transparente), para que el personaje encaje en el fondo. Vamos a convertir los graficos con ConvImgCPC como ya vimos en tutoriales anteriores. Estos son los valores que he usado para el personaje principal:

Ya que el sprite y el fondo deben compartir la misma paleta, debemos marcar en el programa los colores que queremos mantener fijos, antes de abrir el gráfico del fondo. En este caso el sprite usa 10 colores, así que marcamos las 10 casillas, dejándolo así:

A continuación abrimos el fondo y lo convertimos, estos son los valores que he usado:

Una vez tenemos los dos graficos exportados en asm, los convertimos a c como ya hemos visto en otros tutoriales. Quedando de una manera similar a esta:

/*
;Généré par ConvImgCpc Version 0.16
; Mode 0
; 12x23
; Linear
*/

#define SPRITE_WIDTH 12
#define SPRITE_HEIGHT 23
#define NUM_COLORS 16
const unsigned char Palette[NUM_COLORS] = {8,0,26,15,24,6,20,14,11,1,10,12,25,9,13,19};


const char Sprite[] = 
{
               0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0
        ,      0xC0, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xC8, 0x00
        ,      0xC0, 0x84, 0x0C, 0x0C, 0x48, 0xC0, 0x00, 0x00
        ,      0x00, 0xC4, 0xC8, 0xC0, 0x0C, 0x0C, 0x0C, 0x0C
        ,      0x0C, 0x48, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0
        ,      0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xC0, 0x00
        ,      0x00, 0x00, 0x00, 0x84, 0x0C, 0x0C, 0x0C, 0x0C
        ,      0xCC, 0xCC, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x84
        ,      0x0C, 0x0C, 0x0C, 0x4C, 0x30, 0x90, 0x60, 0x00
        ,      0x00, 0x00, 0x00, 0x84, 0x0C, 0x0C, 0x0C, 0x4C
        ,      0x30, 0x90, 0x60, 0x00, 0x00, 0x00, 0x00, 0x84
        ,      0x0C, 0x0C, 0x0C, 0x4C, 0x30, 0x90, 0x60, 0x00
        ,      0x00, 0x00, 0x00, 0x84, 0x0C, 0x0C, 0x0C, 0x0C
        ,      0xCC, 0xCC, 0xC8, 0x00, 0x00, 0x00, 0x00, 0xC0
        ,      0x0C, 0x0C, 0xC0, 0xC0, 0x0C, 0xFC, 0xC0, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xAC, 0x48
        ,      0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0xC1, 0x42, 0x0C, 0x0C, 0x94, 0x68, 0xC0, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0xC1, 0x42, 0x0C, 0x0C
        ,      0x94, 0x3C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0xC0, 0xC0, 0xC0, 0xC0, 0xC4, 0x60, 0xC0, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0xC1, 0x42, 0xF0, 0xF0
        ,      0x94, 0x3C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0xC0, 0xC0, 0xC0, 0xC0, 0x81, 0x42, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0xC0, 0xD4, 0x0C, 0x0C
        ,      0xE8, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0
        ,      0xC0, 0xD4, 0x0C, 0x0C, 0x48, 0xC0, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0
        ,      0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0
        ,      0xC0, 0xD0, 0xF0, 0xF0, 0xF0, 0xC0, 0xC0, 0x00
        ,      0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0
        ,      0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xC0
        ,      0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0
        ,      0xC0, 0xC0, 0x00, 0x00
};
/*
;Généré par ConvImgCpc Version 0.16
; Mode 0
; 80x200
; Linear
*/

const char Background[] = 
{
               0x9B, 0x31, 0x30, 0x33, 0x67, 0x33, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x32
        ,      0x32, 0x32, 0x33, 0x9B, 0x33, 0x30, 0x9B, 0x31
        ,      0x32, 0x33, 0x31, 0x32, 0x30, 0x65, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0xCA, 0xB2, 0x32, 0x33, 0x33
        ,      0x30, 0x31, 0x9A, 0x30, 0x31, 0x32, 0x32, 0x32
        ,      0x31, 0x31, 0x33, 0x30, 0x9B, 0x30, 0x32, 0xCF

        ...
        
          ,      0x32, 0x33, 0x33, 0x33, 0x31, 0x9B, 0x31, 0x32
        ,      0x33, 0x33, 0x33, 0x33, 0x31, 0x9B, 0x31, 0x32
        ,      0x33, 0x33, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x33
        ,      0x32, 0x33, 0x33, 0x33, 0x31, 0x9B, 0x31, 0x32
        ,      0x33, 0x33, 0x33, 0x33, 0x31, 0x9B, 0x30, 0x33
        ,      0x32, 0x33, 0x33, 0x91, 0x33, 0x65, 0x30, 0x32
};

Si aplicamos lo que hemos aprendido hasta ahora en otros tutoriales, pintaríamos el fondo y encima el sprite moviéndose y quedaría de la siguiente manera:

////////////////////////////////////////////////////////////////////////
// sprite01.c
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Sprite.h"
#include "Background.h"

#define MAX_X 79

void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex)
{
  __asm
    ld a, 4 (ix)
    ld b, 5 (ix)
    ld c, b
    call #0xBC32 ;SCR SET INK
    __endasm;
}

void SetPalette(const unsigned char *pPalette)
{
  unsigned char nColor = 0;

  for(nColor = 0; nColor < NUM_COLORS; nColor++)
    SetColor(nColor, pPalette[nColor]);
}

void PutSpriteMode0(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

    _loop_alto:
       PUSH BC
       LD B,C
       PUSH HL
    _loop_ancho:
       LD A,(DE)
       LD (HL),A
       INC DE
       INC HL
       DJNZ _loop_ancho
       POP HL
       LD A,H
       ADD #0x08
       LD H,A
       SUB #0xC0
       JP NC, _sig_linea
       LD BC, #0xC050
       ADD HL,BC
    _sig_linea:
       POP BC
       DJNZ _loop_alto
  __endasm;
}


////////////////////////////////////////////////////////////////////////
unsigned char char1,char2,char3,char4;

unsigned int GetTime()
{
  unsigned int nTime = 0;

  __asm
    CALL #0xBD0D ;KL TIME PLEASE
    PUSH HL
    POP DE
    LD HL, #_char3
    LD (HL), D
    LD HL, #_char4
    LD (HL), E
  __endasm;

  nTime = (char3 << 8) + char4;

  return nTime;
}
////////////////////////////////////////////////////////////////////////

void main()
{
  unsigned char nSprite = 0;
  int nX = 20;
  int nY = 100;
  char nXDir = 1;
  unsigned int nLastMoveTime = GetTime();
  
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;
  
  SetPalette(Palette);

  PutSpriteMode0((unsigned char *)0xC000, 80, 200, Background);
  
  while(1)
  {
    if(GetTime() - nLastMoveTime < 15)
      continue;

    nLastMoveTime = GetTime();

    //move
    nX += nXDir;
      
    if(nX <= 0)
    {
      nX = 0;
      nXDir = 1;
    }
  
    if(nX >= (MAX_X - SPRITE_WIDTH))
    {
      nX = MAX_X - SPRITE_WIDTH;
      nXDir = -1;
    }
  
    //paint
    PutSpriteMode0((unsigned char *)(0xC000 + ((nY / 8u) * 80u) + ((nY % 8u) * 2048u) + nX), SPRITE_WIDTH, SPRITE_HEIGHT, Sprite);
  }
}
////////////////////////////////////////////////////////////////////////

El resultado sería el siguiente:

 

 

Para conseguir la transparencia, vamos a hacer una nueva función para pintar el sprite, basada en la anterior, pero que no pinte los pixeles con color 0 (el rosa) y así funcione la transparencia, el código fuente quedaría así:

////////////////////////////////////////////////////////////////////////
// sprite02.c
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Sprite.h"
#include "Background.h"

#define MAX_X 79

void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex)
{
  __asm
    ld a, 4 (ix)
    ld b, 5 (ix)
    ld c, b
    call #0xBC32 ;SCR SET INK
    __endasm;
}

void SetPalette(const unsigned char *pPalette)
{
  unsigned char nColor = 0;

  for(nColor = 0; nColor < NUM_COLORS; nColor++)
    SetColor(nColor, pPalette[nColor]);
}

void PutSpriteMode0(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

    _loop_alto:
       PUSH BC
       LD B,C
       PUSH HL
    _loop_ancho:
       LD A,(DE)
       LD (HL),A
       INC DE
       INC HL
       DJNZ _loop_ancho
       POP HL
       LD A,H
       ADD #0x08
       LD H,A
       SUB #0xC0
       JP NC, _sig_linea
       LD BC, #0xC050
       ADD HL,BC
    _sig_linea:
       POP BC
       DJNZ _loop_alto
  __endasm;
}

void PutSpriteMode0Trans(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

_loop_alto2:
    PUSH BC
    LD B,C
    PUSH HL
_loop_ancho2:
    LD A,(DE)
    CP #0
    JP Z, _notpaint
    LD (HL),A
_notpaint:
    INC DE
    INC HL
    DJNZ _loop_ancho2
    POP HL
    LD A,H
    ADD #0x08
    LD H,A
    SUB #0xC0
    JP NC, _sig_linea2
    LD BC, #0xC050
    ADD HL,BC
_sig_linea2:
    POP BC
    DJNZ _loop_alto2
    __endasm;
}

////////////////////////////////////////////////////////////////////////
unsigned char char1,char2,char3,char4;

unsigned int GetTime()
{
  unsigned int nTime = 0;

  __asm
    CALL #0xBD0D ;KL TIME PLEASE
    PUSH HL
    POP DE
    LD HL, #_char3
    LD (HL), D
    LD HL, #_char4
    LD (HL), E
  __endasm;

  nTime = (char3 << 8) + char4;

  return nTime;
}
////////////////////////////////////////////////////////////////////////

void main()
{
  unsigned char nSprite = 0;
  int nX = 20;
  int nY = 100;
  char nXDir = 1;
  unsigned int nLastMoveTime = GetTime();
  
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;
  
  SetPalette(Palette);

  PutSpriteMode0((unsigned char *)0xC000, 80, 200, Background);
  
  while(1)
  {
    if(GetTime() - nLastMoveTime < 15)
      continue;

    nLastMoveTime = GetTime();

    //move
    nX += nXDir;
      
    if(nX <= 0)
    {
      nX = 0;
      nXDir = 1;
    }
  
    if(nX >= (MAX_X - SPRITE_WIDTH))
    {
      nX = MAX_X - SPRITE_WIDTH;
      nXDir = -1;
    }
  
    //paint
    PutSpriteMode0Trans((unsigned char *)(0xC000 + ((nY / 8u) * 80u) + ((nY % 8u) * 2048u) + nX), SPRITE_WIDTH, SPRITE_HEIGHT, Sprite);
  }
}
////////////////////////////////////////////////////////////////////////

El resultado sería el siguiente:

 

 

Como puede verse el sprite va dejando una estela tras de sí, borrando el fondo... Muchos pensarían que lo más sencillo sería pintar siempre el fondo completo y luego encima el sprite, pues vamos a comprobar que pasaría. El código fuente quedaría así:

////////////////////////////////////////////////////////////////////////
// sprite03.c
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Sprite.h"
#include "Background.h"

#define MAX_X 79

void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex)
{
  __asm
    ld a, 4 (ix)
    ld b, 5 (ix)
    ld c, b
    call #0xBC32 ;SCR SET INK
    __endasm;
}

void SetPalette(const unsigned char *pPalette)
{
  unsigned char nColor = 0;

  for(nColor = 0; nColor < NUM_COLORS; nColor++)
    SetColor(nColor, pPalette[nColor]);
}

void PutSpriteMode0(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

    _loop_alto:
       PUSH BC
       LD B,C
       PUSH HL
    _loop_ancho:
       LD A,(DE)
       LD (HL),A
       INC DE
       INC HL
       DJNZ _loop_ancho
       POP HL
       LD A,H
       ADD #0x08
       LD H,A
       SUB #0xC0
       JP NC, _sig_linea
       LD BC, #0xC050
       ADD HL,BC
    _sig_linea:
       POP BC
       DJNZ _loop_alto
  __endasm;
}

void PutSpriteMode0Trans(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

_loop_alto2:
    PUSH BC
    LD B,C
    PUSH HL
_loop_ancho2:
    LD A,(DE)
    CP #0
    JP Z, _notpaint
    LD (HL),A
_notpaint:
    INC DE
    INC HL
    DJNZ _loop_ancho2
    POP HL
    LD A,H
    ADD #0x08
    LD H,A
    SUB #0xC0
    JP NC, _sig_linea2
    LD BC, #0xC050
    ADD HL,BC
_sig_linea2:
    POP BC
    DJNZ _loop_alto2
    __endasm;
}

////////////////////////////////////////////////////////////////////////
unsigned char char1,char2,char3,char4;

unsigned int GetTime()
{
  unsigned int nTime = 0;

  __asm
    CALL #0xBD0D ;KL TIME PLEASE
    PUSH HL
    POP DE
    LD HL, #_char3
    LD (HL), D
    LD HL, #_char4
    LD (HL), E
  __endasm;

  nTime = (char3 << 8) + char4;

  return nTime;
}
////////////////////////////////////////////////////////////////////////

void main()
{
  unsigned char nSprite = 0;
  int nX = 20;
  int nY = 100;
  char nXDir = 1;
  unsigned int nLastMoveTime = GetTime();
  
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;
  
  SetPalette(Palette);

  PutSpriteMode0((unsigned char *)0xC000, 80, 200, Background);
  
  while(1)
  {
    if(GetTime() - nLastMoveTime < 15)
      continue;

    nLastMoveTime = GetTime();

    //move
    nX += nXDir;
      
    if(nX <= 0)
    {
      nX = 0;
      nXDir = 1;
    }
  
    if(nX >= (MAX_X - SPRITE_WIDTH))
    {
      nX = MAX_X - SPRITE_WIDTH;
      nXDir = -1;
    }
  
    //paint
    PutSpriteMode0((unsigned char *)0xC000, 80, 200, Background);
    PutSpriteMode0Trans((unsigned char *)(0xC000 + ((nY / 8u) * 80u) + ((nY % 8u) * 2048u) + nX), SPRITE_WIDTH, SPRITE_HEIGHT, Sprite);
  }
}
////////////////////////////////////////////////////////////////////////

El resultado sería el siguiente:

 

 

Como puede verse este método ademas de lento provoca un parpadeo horrible. Como estamos trabajando directamente sobre la memoria de video es imposible evitar el parpadeo usando este método. Para solucionar el parpadeo y el tener que pintar todo el fondo cada vez vamos a probar a usar un doble buffer, pero sólo del tamaño del sprite, no de la pantalla. Lo que haremos es volcar el trozo de fondo sobre el doble buffer, pintar en el de manera transparente el sprite y finalmente volcar el doble buffer en la pantalla. En este caso he utilizado una mezcla de métodos en C y en Asm, muy sencillos y que ilustran perfectamente el método usado. El código fuente completo quedaría así:

////////////////////////////////////////////////////////////////////////
// sprite04.c
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Sprite.h"
#include "Background.h"

#define MAX_X 79

const char SpriteBuffer[SPRITE_WIDTH * SPRITE_HEIGHT] = {0};

void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex)
{
  __asm
    ld a, 4 (ix)
    ld b, 5 (ix)
    ld c, b
    call #0xBC32 ;SCR SET INK
    __endasm;
}

void SetPalette(const unsigned char *pPalette)
{
  unsigned char nColor = 0;

  for(nColor = 0; nColor < NUM_COLORS; nColor++)
    SetColor(nColor, pPalette[nColor]);
}

void PutSpriteMode0(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

    _loop_alto:
       PUSH BC
       LD B,C
       PUSH HL
    _loop_ancho:
       LD A,(DE)
       LD (HL),A
       INC DE
       INC HL
       DJNZ _loop_ancho
       POP HL
       LD A,H
       ADD #0x08
       LD H,A
       SUB #0xC0
       JP NC, _sig_linea
       LD BC, #0xC050
       ADD HL,BC
    _sig_linea:
       POP BC
       DJNZ _loop_alto
  __endasm;
}

void PutSpriteLinealTrans(unsigned char *pAddress, unsigned char *pSprite, unsigned int nBytes)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD E, 6(IX) 
    LD D, 7(IX)            
    LD C, 8(IX) 
    LD B, 9(IX) 

_loop:
    LD A,(DE)
    CP #0
    JP Z, _notpaint
    LD (HL),A
_notpaint:
    INC DE
    INC HL
    DEC BC
    LD A, #0
    ADD A, B
    ADD A, C
    JP NZ, _loop
    __endasm;
}

////////////////////////////////////////////////////////////////////////
unsigned char char1,char2,char3,char4;

unsigned int GetTime()
{
  unsigned int nTime = 0;

  __asm
    CALL #0xBD0D ;KL TIME PLEASE
    PUSH HL
    POP DE
    LD HL, #_char3
    LD (HL), D
    LD HL, #_char4
    LD (HL), E
  __endasm;

  nTime = (char3 << 8) + char4;

  return nTime;
}
////////////////////////////////////////////////////////////////////////

void main()
{
  unsigned char nSprite = 0;
  int nX = 20;
  int nY = 100;
  char nXDir = 1;
  unsigned int nLastMoveTime = GetTime();
  int nAux = 0;
  
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;
  
  SetPalette(Palette);

  PutSpriteMode0((unsigned char *)0xC000, 80, 200, Background);
  
  while(1)
  {
    if(GetTime() - nLastMoveTime < 10)
      continue;

    nLastMoveTime = GetTime();

    //move
    nX += nXDir;
      
    if(nX <= 0)
    {
      nX = 0;
      nXDir = 1;
    }
  
    if(nX >= (MAX_X - SPRITE_WIDTH))
    {
      nX = MAX_X - SPRITE_WIDTH;
      nXDir = -1;
    }
  
    //copy background to sprite double buffer
    for(nAux = 0; nAux < SPRITE_HEIGHT; nAux++)
      memcpy(SpriteBuffer + nAux * SPRITE_WIDTH, Background + 80 * (nY + nAux) + nX, SPRITE_WIDTH);
    
    //paint sprite to double buffer with transparency
    PutSpriteLinealTrans(SpriteBuffer, Sprite, SPRITE_WIDTH * SPRITE_HEIGHT);

    //paint the result to screen
    PutSpriteMode0((unsigned char *)(0xC000 + ((nY / 8u) * 80u) + ((nY % 8u) * 2048u) + nX), SPRITE_WIDTH, SPRITE_HEIGHT, SpriteBuffer);
  }
}
////////////////////////////////////////////////////////////////////////

El resultado sería el siguiente:

 

Como vemos, esta vez si que queda bien, evitando parpadeos innecesarios.

Podéis bajar un zip con todos ficheros (código fuente, bat's para compilar, binarios y dsk's) aquí: Sprites_III.zip

 

www.CPCMania.com 2012