Campo de estrellas 2D

Click here to see in English

En el tutorial anterior Pintando pixeles: Introducción a la memoria de video aprendimos como pintar pixeles independientes en la pantalla, vamos a usarlo para generar un sencillo y bonito efecto de campo de estrellas.

Para hacerlo vamos primero a fijar el modo 0 y cambiar la paleta, para usar los colores negro, amarillo, amarillo pastel y amarillo brillante. Usaremos el negro para el fondo, y los tres tonos de amarillo para distinguir estrellas más o menos lejanas y que se mueven con mayor o menor velocidad. Como ya vimos en anteriores tutoriales, para cambiar al modo 0, lo hacemos desde ensamblador, usando el comando del firmware SCR SET MODE (BC0E), para la paleta lo haremos con el comando del firmware SCR SET INK (BC32) y para dejar el borde en negro también usaremos el comando SCR SET BORDER (BC38), de la siguiente forma:

//SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //PALETE
  __asm
    ld a, #0
    ld b, #0 ;black
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #1
    ld b, #12 ;Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #2
    ld b, #25 ;Pastel Yellow    
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #3
    ld b, #24 ;Bright Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK
  __endasm;

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

Para las estrellas usaremos una estructura con la posición y el tipo de estrella y el bucle principal básicamente lo que hará es recorrerse todas las estrellas, borrar el pixel de la posición anterior, actualizar la posición y volver a pintar la estrella. Mejor vemos directamente el código fuente completo, para sdcc (al final de tutorial se puede descargar un zip):

////////////////////////////////////////////////////////////////////////
// starfield.c
// 2D Star Field
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void SetMode0PixelColor(unsigned char *pByteAddress, unsigned char nColor, unsigned char nPixel)
{
  unsigned char nByte = *pByteAddress;

  if(nPixel == 0)
  {
    nByte &= 85;

    if(nColor & 1)
      nByte |= 128;

    if(nColor & 2)
      nByte |= 8;

    if(nColor & 4)
      nByte |= 32;

    if(nColor & 8)
      nByte |= 2;
  }
  else
  {
    nByte &= 170;

    if(nColor & 1)
      nByte |= 64;

    if(nColor & 2)
      nByte |= 4;

    if(nColor & 4)
      nByte |= 16;

    if(nColor & 8)
      nByte |= 1;
  }

  *pByteAddress = nByte;
}

void PutPixelMode0(unsigned char nX, unsigned char nY, unsigned char nColor)
{
  unsigned char nPixel = 0;
  unsigned int nAddress = 0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 2);
  nPixel = nX % 2;

  SetMode0PixelColor((unsigned char *)nAddress, nColor, nPixel);
}

struct _tStar
{
  unsigned char nX;
  unsigned char nY;
  unsigned char nStarType;
};

#define STARS_NUM 40
struct _tStar aStars[STARS_NUM];

void main()
{
  unsigned char nStar = 0;
  memset(aStars, 0, sizeof(aStars));
  
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //PALETE
  __asm
    ld a, #0
    ld b, #0 ;black
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #1
    ld b, #12 ;Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #2
    ld b, #25 ;Pastel Yellow    
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #3
    ld b, #24 ;Bright Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK
  __endasm;

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

  //Init
  for(nStar = 0; nStar < STARS_NUM; nStar++)
  {
    aStars[nStar].nX = rand() % 160;
    aStars[nStar].nY = rand() % 200;
    aStars[nStar].nStarType = rand() % 3;
  }

  while(1)
  {

    for(nStar = 0; nStar < STARS_NUM; nStar++)
    {
      //delete star
      PutPixelMode0(aStars[nStar].nX, aStars[nStar].nY, 0);

      //move star
      switch(aStars[nStar].nStarType)
      {
        case 0: //slow star
          aStars[nStar].nX += 1;
          break;
        case 1: //medium star
          aStars[nStar].nX += 2;
          break;
        case 2: //fast star
          aStars[nStar].nX += 3;
          break;
      }
      
      if(aStars[nStar].nX >= 160)
      {
        aStars[nStar].nX = 0;
        aStars[nStar].nY = rand() % 200;
        aStars[nStar].nStarType = rand() % 3;
        continue;
      }

      //paint star
      PutPixelMode0(aStars[nStar].nX, aStars[nStar].nY, aStars[nStar].nStarType + 1);
    }
  }
}
////////////////////////////////////////////////////////////////////////

Compilamos y generamos dsk con los siguientes comandos (starfield.bat):

sdcc -mz80 --code-loc 0x6038 --data-loc 0 --no-std-crt0 crt0_cpc.rel starfield.c
hex2bin starfield.ihx
CPCDiskXP -File starfield.bin -AddAmsdosHeader 6000 -AddToNewDsk starfield.dsk    
    

Ejecutamos en el emulador y obtenemos algo similar a esto (en el emulador no da tirones):

fill

Está limitado a 40 estrellas simultaneas, ya que si ponemos más empiezan los tirones y habría que optimizar el código e implementar partes en ensamblador... quizás en un futuro tutorial.

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

 

www.CPCMania.com 2012