In this tutorial, we will cover some of the more basics of
- Simple sound output in TRSE
- Sprite handling
- Joystick input
- Program structure
In this program, we will display two sprites. One will move about using the joystick while making walking sounds and pooping a hearty bullet.
The program starts off without any fuzz
PROGRAM tutorial3;
VAR
a,b, c, i, j,k,val,mainloop, time : byte;
x,y : byte;
sprite0data: IncBin("data/sprite_blob.bin");
sprite_time : integer;
sprite_x : integer;
sprite_y : integer;
bullet_x : integer;
bullet_y : integer;
The only thing worth noting here is the IncBin function, which inclues binary files directly into the assembler. In addition, IncBin lets the user specify both load address and load offset of binary files. The sprites were created with CBM PRG Studio, and contains 3 sprites of each 64 bytes. Wonderful. I seriously needed four beers before managing to produce this lovely piece of art.
procedure InitializeSprites();
begin
sprite_x:=160;
sprite_y:=180;
bullet_x:=400;
bullet_y:=180;
setSpriteLoc(0, $0D);
setSpriteLoc(1, $0E);
poke(SPRITE_BITMASK,0, %00000011);
poke(SPRITE_COLOR,0, YELLOW);
poke(SPRITE_COLOR,1, GREEN);
memcpy(sprite0data, 0, SPRITE_LOC1, 63);
memcpy(sprite0data, 128, SPRITE_LOC2, 63);
end;
This method intializes the sprites. Note that
- Integers can have >255 assignments
- SetSpriteLoc takes two constants: the sprite ID together with a fixed memory location, which really is multiplied with 64. The new calculated value is automatically stored in the SPRITE_LOC1-9 constant
- BITMASK regulates which sprites are displayed. For instance, #%00000001 would only display sprite 1, while #%10010001 would display sprite 8, 5 and 1.
- The memcpy function has a handy memory shift: it loads from the memory position of [parameter 1 + parameter 2], and copies [parameter 4] values to [parameter 3]. In this case, sprite 1 data is copied into sprite 1 memory location, and sprite 3 data is copied in to sprite 2 memory location. I wish you had more memories of me.
procedure MoveSoundEffect(freq_move:byte);
begin
//Volume, hi byte freq, attack voice 1, sustain=16* + release, waveform, release waveform
PlaySound(SID_CHANNEL1,
7, // Volume
freq_move, // Hi byte frequency
0*16+0, // Attack voice 1
3*16 + 3, // Sustain = 16*15 + release=6
1 +SID_NOISE, // Waveform
SID_NOISE); // waveform
end;
The PlaySound method let’s the user play simple sounds through the SID chip. The three channels are supported through constants SID_CHANNEL1-3, and waveforms are SID_NOISE, SID_TRI, SID_PULSE and SID_SAW. The parameters of PlaySound are as such:
- Channel 1-3
- Volume 0-15
- Hi byte of frequency
- Attack voice
- Sustain *16 + release
- Waveform
procedure MoveSprite();
begin
spritepos(sprite_x, sprite_y, 0);
Joystick();
sprite_x := sprite_x - joystickleft*2;
sprite_x := sprite_x + joystickright*2;
sprite_y := sprite_y + joystickdown*2;
sprite_y := sprite_y - joystickup*2;
rand(15, 45, x);
if (joystickleft=1 or joystickright=1) then
MoveSoundEffect(x);
if (joystickup<>0 or joystickdown <>0) then
MoveSoundEffect(x);
if joystickbutton=1 then begin
ShootSoundEffect(SID_SAW, 15);
bullet_x:=sprite_x;
bullet_y:=sprite_y;
end;
end;
This method sets the sprite #0 position, and increases the x/y values based on joystick input. For the joystick input to be updated, the Joystick() method needs to be called. Repeatedly. Like, when you have to call your ex 60 times per second repeatedly. In addition, when the joystick is moved, a sound is played. The same happens when the joystick button is pressed, and the bullet position is set to the current player position.
procedure UpdateSprite();
begin
sprite_time := sprite_time +1;
if sprite_time>20 then begin
sprite_time:=0;
end;
if sprite_time=10 then begin
setSpriteLoc(0, $3C, 0);
end;
if sprite_time=0 then begin
setSpriteLoc(0, $3D, 0);
end;
end;
This method increases a global timer and sets the sprite data accordingly, toggling between the two sprites of our fabulous protagoinist.
begin
poke(SCREEN_BG_COL, 0, RED);
poke(SCREEN_FG_COL, 0, BLACK);
ClearScreen($20, SCREEN_CHAR_LOC);
mainloop:=1;
time:=0;
sprite_time := 0;
InitializeSprites();
While 1<>2 do begin
time:=time+1;
UpdateSprite();
PrintText();
MoveSprite();
MoveBullet();
WaitForRaster(1);
end;
END.
The main routine. Note a couple of things:
- There is a simple DrawText procedure that will render the time (in hex format) to the screen
- The WaitRaster will wait for the raster line to hit line 0 before continuing, maintaining a healthy 60 frames per second.