CMM2 graphics examples and explanation


Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11176
Posted: 11:10am 24 May 2020      

Sorry about the delay in getting to this post - people keep finding bugs that need fixing

In this post I will look at some of the Basics of sprites and then the final sprite post will look at collisions. I might then do one more post on 3D graphics and try to de-mystify some of the maths behind them.

In the previous post we saw what a sprite is and how to create one so we can start here by getting rid of one or more sprites. This may be important to free up the 64 sprite slots for new sprites and the syntax is obvious

  Quote  SPRITE CLOSE [#]n
SPRITE CLOSE ALL


Executing these commands frees up the sprite slot and the memory used by the sprite. There is one important difference in their usage. Closing an individual sprite will also remove it from the screen if it is visible. Closing all sprites leaves the screen untouched as this is likely only to be used at the end of a program or when switching to a completely new context in a game. It is also important to note that you can't close a sprite from which copies have been made until all of the copies have been closed.

So we can create sprites and destroy them but we now need to know how to use them.

There is one overriding rule to be followed to make things work:

While sprites are in use all sprite commands must be done with the same PAGE WRITE set

This need not be PAGE 0, the display page, but must be consistent for all SPRITE commands. You can move to other pages and use other graphics commands but if any sprite is active then all subsequent sprite commands must be to that page.

To keep things simple for the moment we will not change the page we are writing to and leave it at PAGE 0. I've attached the sprite file for the red mouse pointer we saw earlier and an image to use in the next example. NB: you need at least V5.05.02RC39 to run this example due to a bug in earlier versions

mouse.zip


'set to 320x200 16 bit colour
mode 3,16

' load the mouse cursor as sprite number 1
sprite load "mouse.spr",1

'load a background image
load bmp "tiger320"

'set up some variables to track the sprite position and show it
dim integer x=mm.hres/2, y=mm.vres/2
sprite show 1, x, y, 1

'loop forever
do

'use the keydown function with the USB keyboard to check for a keypress
'you can change this to use inkey$ to use a serial console
 if keydown(0) then 'There is a key pressed so check for one of the arrow keys
   if keydown(1)=128 then  y=y-1
   if keydown(1)=129 then  y=y+1
   if keydown(1)=130 then  x=x-1
   if keydown(1)=131 then  x=x+1

' use the sprite function to check if the sprite needs to move
' by comparing its actual position to the new x,y position
' if it is different then display the sprite in the new position
' this will automatically restore the background overwritten by the original sprite
' and then rewrite it in the new position

   if x<> sprite(x,1) or y <> sprite(y,1) then sprite show 1, x, y, 1

 endif
 pause 10
loop





Try keeping the left arrow pressed and watch as the sprite leaves the screen

[12] Error: -13 is invalid (valid is -12 to 319)


Oops: sprites must have at least one pixel of their containing rectangle on the screen otherwise you will get an error. We can easily check for this in the code

   if keydown(1)=128 and sprite(y,1) + sprite(h,1) > 1 then  y=y-1
   if keydown(1)=129 and sprite(y,1)+1 < MM.VRES then  y=y+1
   if keydown(1)=130 and sprite(x,1) + sprite(w,1) > 1 then  x=x-1
   if keydown(1)=131 and sprite(x,1)+1 < MM.HRES then  x=x+1


The SPRITE function is very useful for telling us about the sprite. Check the manual for all the various SPRITE function parameters.

The important command we have used here is SPRITE SHOW. This command is used both to display a sprite and to move it. However it has two additional parameters that we haven't yet considered.

SPRITE SHOW [#n], x, y, layer [,orientation]


The optional orientation parameter acts to modify the sprite as it is displayed in the same way as we saw for the BLIT command, 0=normal, 1=mirrored left to right, 2=mirrored top to bottom, 3=rotated 180 degrees.

The mandatory (collision) layer parameter does two things for us relating to collisions and movement and can be set between 0 and 10.

Sprites shown on layer 0 will move with the background if we use SPRITE SCROLL or SPRITE SCROLLR. Sprites on all other layers will remain fixed on the screen during a scroll and the background will move underneath them

Sprites collisions are detected when a sprite overlaps another sprite on the same layer or layer 0 so collisions can be caused by moving a sprite on any layer or scrolling the background such that a layer 0 sprite now overlaps a sprite on any of the other layers.

Collisions will be discussed in much more detail in the next post but there are other sprite commands we should look at first.

SPRITE WRITE and BLIT WRITE do exactly the same thing. In both case the SPRITE is written out but the background image isn't stored first and you can WRITE the same sprite as many times as you wish to different locations on the screen. In essence the command is just using the sprite as a in memory image that can be used whenever required.

SPRITE HIDE does what it says, it removes a sprite from the screen and restores the background we can add this to our example program and make the cursor flash

  Quote  mode 3,16
sprite load "mouse.spr",1
load bmp "tiger320"
dim integer x=mm.hres/2, y=mm.vres/2
settick 100,flash
sprite show 1, x, y, 1
do
 if keydown(0) then
   if keydown(1)=128 and sprite(y,1) + sprite(h,1) > 1 then  y=y-1
   if keydown(1)=129 and sprite(y,1)+1 < MM.VRES then  y=y+1
   if keydown(1)=130 and sprite(x,1) + sprite(w,1) > 1 then  x=x-1
   if keydown(1)=131 and sprite(x,1)+1 < MM.HRES then  x=x+1
 endif
 pause 20
loop

sub flash
 static a=1
 if a then
   sprite show 1,x,y,1
   a=0
 else
   sprite hide 1
   a=1
 endif
end sub


SPRITE NEXT and SPRITE MOVE commands allow you to create a sequence of sprite actions which will be executed as a single atomic transaction. This was developed for other platforms to avoid tearing and flashing effects but because on the CMM2 sprites can be moved on non-visible pages these commands are not as important.

The SPRITE SCROLL and SPRITE SCROLLR commands allow you to move the background image under the sprites for the whole screen or just part of it. SPRITE SCROLL is really identical to PAGE SCROLL that we discussed in detail above with the simple exception that all sprites on layers 1 to 10 are automatically hidden before the scroll and then replaced in their original positions afterwards. SPRITE SCROLLR does exactly the same thing but allows you to specify a part of the background image to scroll

The last sprite command we will consider in this post is SPRITE TRANSPARENCY and this is used to create the ghost demo

The full code for this is attached and explained below. This uses many of the concepts we have seen in the posts on this thread and brings them together. What I hope will be clear from reading the code is how easy it is to create stunning effects with the CMM2 once you understand a little about the graphics are designed and work.

Image files
2020-05-08_234454_Ghost.zip



'set to 640x400 mode with 2 video layers and 12-bit colour. Page 0 is the bottom layer and page 1 is the top
mode 2,12

'clear the first three framebuffers (pages)
for i=0 to 2:page write i:cls:next i

'load a sprite which is the image of the ghost from a png file.
'The fact it is a png is important as png files encode transparency as well as solid colours
sprite loadpng 1,"ghost"

'set that we are going to write to the background layer
page write 0

'load the background image to the background layer - page 0
load png "part02"

'The image I am using is only 320x200 so I'm going to resize it to fit the screen
image resize 0,0,320,200,0,0,640,400

'initialise the display position of the ghost
x=100
y=50

'initialise the transparency of the ghost
' transparencies go from 1 (nearly invisible) to 15 (solid colour)
t=8

'set to write to page 2 which is not being displayed
page write 2

' output the ghost on page 2
sprite show 1,x,y,1


i=0

'start the main process loop
do

'do some silly maths to create a random walk of the ghost in both position and
' transparency while keeping it within the display bounds and the transparency
' within useful limits
i=i+1
if i mod 5 = 0 then c=rnd()-0.5
if i mod 3 = 0 then a=rnd()*8-4
if i mod 3 = 0 then b=rnd()*6-3
x=x+a
if x<0 then x=0
if x>MM.HRES-sprite(w,1) then x=mm.hres-sprite(w,1)
y=y+b
if y<0 then y=0
if y>MM.VRES-sprite(h,1) then y=mm.vres-sprite(h,1)
t=t+c
if t<3 then t=3
if t>12 then t=12

'display the sprite in the new position and with the new transparency
sprite transparency 1,t
sprite show 1,x,y,1

'now copy page 2 to the foreground layer during frame blanking
'this ensures that there are no tearing effects in the image

page copy 2 to 1,b

'slow things down a bit, the CMM2 is too fast
pause 100
loop