M/C Part 22 Game Structure. --------------- 99.9% of games will most probably use the following basic outline from development to completion (well, mine do anyway): Front End: This is the first thing the game player will see when He/She loads the game.And should have the following features/options. Players: Selection of number of players (can be 1 to 1 billion if you want) Control: Enabling the player(s) to change from Keys,Mouse or Joystick control Music: Enabling the player to change or turn music on/off Play: Usually upon the player pressing fire/mouse button and will then continue into the game program Sometimes there may be options to view high score tables,see intros,quit etc. These options depend upon the type/stye of the program. Game Time: Upon selection from front end to play game the program should end up here.Then we should go through the following procedures. Initialisation: Here we set up and varables,tables, screens,etc before allowing the player to see the game screen. And also setup inturpts for the options from the Front End Game Loop: In this section we would try to perform the folling list of comands Get Input Status: Get the current input results from the interrupt program. Scrolling: Some games require that the play area be scrolled and it is important to scroll before any Sprite Control is done, else the sprites will wobble about on the screen. Sprite Control: Move Player(s) Sprite(s) with the infomation given from the above Sub-Program + animate if it does. Move + Animate any computer controlled sprites. If any exist, that is. Colision detection. This can be with the following list. Sprite to Sprite Sprite to Background Displaying: After all the player would like to see something every now and then. Test for end: This is when we check to see if the player has lost all their lives,energy etc then we jump to End Game. Or if the game has been completed, then we Jump to Complete Game. If we reach here then return to Game Loop End Game: If we reach here then the player has died or completed the game so do one or all of : Display end of game: Say end of game or something if player has died Hiscore: if avaliable check if reach a place in hiscore and ask for name input. Display Hiscore: Only if you did not do above If you get here Jump to Front End unless there's something else you would like to do. Complete Game: If we reach here have we completed all the game of just a level. if only level complete increase level no and goto Game start. if end of game then show message because its nice for the player to get some reward. Then goto End game. So after all that the basics of the game should look like this (We hope) FRONT END --------- MAIN_START: CALL SETUP_SCR ;Display main screen if any MAIN_LOOP: CALL FLYBACK ;Wait for frame int CALL CHECK_OPTION ;Check for option selction LD A,(FIRE) ;Test for fire pressed OR A JR Z,MAIN_LOOP ;If fire=0 then no JP GAME_START ;Goto game program GAME TIME --------- GAME_START: CALL RESET_GAME ;reset vars for new game NEXT_START: CALL INIT_GAME ;setup screen to new level GAME_LOOP: CALL GET_INPUT ;get control status CALL SCROLL ;if needed scroll screen CALL SPRITE_CONT ;move etc. for sprites CALL SCREEN ;display sprites etc. LD A,(COMPLETE) ;test if completed game OR A JP NZ,COMP_GAME ;end of game reached LD A,(END) ;test if end of player OR A JP NZ,END_GAME ;test for player died JP GAME_LOOP ;go round again COMPLETED GAME -------------- GAME_COMP: CP -1 ;if end the Complete = -1 JP NZ,NEXT_LEV ;else Complete = next level CALL DISPLAY_END ;show end of message COMP_WAIT: LD A,(FIRE) ;Test for fire pressed OR A JR Z,COMP_WAIT ;If fire=0 then no JP GAME_END ;Goto game program NEXT LEVEL ---------- NEXT_LEV: LD (LEVEL),A ;set level no JP NEXT_START END OF GAME ----------- GAME_END: LD A,(COMPLETE) CP -1 CALL NZ,END_MES ;say sorry you died CALL HISCORE END_WAIT: LD A,(FIRE) ;Test for fire pressed OR A JR Z,END_WAIT ;If fire=0 then no JP MAIN_START ;Goto start of program Well thats the basic's of the game out of the way, now as promised a small discribtion of SPRITE CONTROL,(as this is what most people ask me about). First and formost a Sprite needs to have a table of inform- ation which tells the programmer all about the sprite. This can be of any size but it is better to keep it as compact as poss. The list below shows a basic table for a Sprite. XCOORD: DW 0 ;x position of sprite YCOORD: DW 0 ;y position of sprite SPRITE: DW 0 ;frame no of sprite FLAGS: DB 0 ;bit wize state of sprite VAR1: DB 0 ; VAR2: DB 0 ; VAR3: DB 0 ; VAR4: DB 0 ; VAR5: DB 0 ; VAR6: DB 0 ; VAR7: DB 0 ; PROGRAM: DW 0 ;address of control program Almost any form of sprite can use the above table and it is only 16 bytes long. Here is a discription of all entrys: XCOORD+YCOORD: With a Word this allows the sprite to go of the screen if needed and is easily check by testing the high byte of the coordinate. SPRITE: This allows more that 256 different imag- es in the game and give room for smoother animation to be achieved. FLAGS: This is for any of the following statues to affect the sprite: BIT DISCRIPTION STATE 0 Facing left/right 0/1 1 Alive/Died+Exploding 0/1 2 Printable/offscreen 0/1 3 4 5 6 7 VAR1-VAR7: These are spare bytes that may be needed durring development of a game. E.G. Hit count Attack pattern Computer control/player contol PROGRAM: This is the address of the program that is controling the sprite as some sprites only require small amount of checking/ movement etc. E.G. A bullet goes untill off screen or hits somthing. Where as a space ship moves in all eight directions and can hit back- gounds,bullets,ships etc Most of the table will become more apperant when we start the full development of the game. (Which I am still trying to decide which game to do). Now onto the main sprite control program. This is the sub- routine that calls the control programs for all the sprites. Basically what we do is have all our SPRITE TABLES follow each other in memory so we have to start at the first sprite in the sequence and continue untill we have delt with all the sprites and this will go something like this: SPRITE_CONT: LD HL,SPRITE_TABS ;Address of sprite tabs LD A,(SPRITE_CNT) ;No of sprites in game SPRITE_LOOP: EX AF,AF' ;save counter PUSH HL ;save address LD DE,M.XCOORD ;address of copy area LD BC,16 ;bytes per sprite LDIR ;copy them to copy area LD HL,(M.PROGRAM) ;get program address JP (HL) ;and jump to it SPRITE.RET: POP DE ;retrive address in DE LD HL,M.XCOORD ;address of copy area LD BC,16 LDIR ;copy then back to adr EX DE,HL ;replace address to HL EX AF,AF' ;retrive counter DEC A ;and decrease JR NZ,SPRITE_LOOP ;if not zero the repeat RET The copy area is identical to our Sprite info table but has a M. infront of the label this enables us to get a value with out using the Index Register IY and doing things like: LD L,(IY+0) LD H,(IY+1) To get the xcoord of the sprite but now we can do: LD HL,(M.XCOORD) This is quicker and also is easier to understand what is happening when we look throught or debuggging the program. We will look more closely at the precific sprite programs when we are writing the game as the programs vary depending upon the type and action the sprite is performing. Now lets looks a the different possiblitys of printing a sprite (or any thing else) onto the screen. There are several different methods some have been covered on Fred and there are only two requirements to consider when you are writing a sprite print routine: 1:Do we require 100% speed over memory (like Stefan Drissen sprite builder in Issue 29) 2:Or can we sacrifice a small amout of speed for versatlity So lets look at these two a bit mor detail. Option one advantages 100% speed,simplicity Option one disavatages One routine one sprite,two routines two sprites Etc. Can only clip of screen if you have seperate routines to print only parts of sprite. E.G. if sprite is 8 bytes wide and 16 bytes deep then you will need: 1 routine print all 7 routines clip left 7 routines clip right clip top + bottom will take ((16*2)*+(8*2))*8) more routines. Option one result This is perfect for games with little or no animation and the sprites stay within the screen area like: Bats'n'balls Parallax Boing Etc.. But no good for: Prince Of Persia Lemmings Populous XenonII Etc.. Option two advantages Small loss in speed,can store + replace background can clip,not limited to one sprite and uses small amounts of memory. Option two disadvanges Not 100% fast. With respect to Stefan on his EXCELENT Sprite Builder I will only be using Option two for its ease and secondly I hope to teach you how to write your own games, and not just use ever- body else's routines. So as we have explain why I will be using this method,I will now show you some different ways of achiving the required result of displaying a sprite. 1: The plonk on screen method (LDI METHOD) This is simple easy a cheap way of doing this as it erase's what was below (so speed increase for no store/replace of background). but leaves a horrible box around the image. EXAMPLE: LD HL,DATA LD DE,SCREEN LD B,DEPTH DEPTH: PUSH BC ; PUSH HL ;STORE VALUES PUSH DE ; LDI ;*WITDH POP HL ;GET SCREEN IN HL LD DE,128 ; ADD HL,DE ;NEXT LINE MODE 4 POP DE ;GET DATA IN DE EX DE,HL ;SWOP DE WITH HL POP BC ;GET DEPTH DJNZ DEPTH ;DECREASE AND REPEAT 2: The mask using mask table (STACK METHOD) This is great if you area very short on memory and can only store the pixel data of the image, But it is slowish and uses teh stack pointer so inturpts must be disabled, And you cannot use pen 0 in you sprite data as this will let the background show through. EXAMPLE: LD HL,SCREEN LD SP,DATA LD BC,ANDTABLE EXX LD B,DEPTH DEPTH: EXX ;REPEAT FOR WITDH COUNT/2 POP DE ;GET TWO DATA BYTES LD C,E ;SET UP MASK TAB ADRESS LD A,(BC) ;GET MASK BYTE AND (HL) ;MAKS WITH SCREEN OR C ;MIX WITH DATA LOW LD (HL),A ;PLACE ON SCREEN INC L ;NEXT SCREEN LOCATION LD C,D ;SET UP MASK TAB ADRESS LD A,(BC) ;GET MASK BYTE AND (HL) ;MAKS WITH SCREEN OR C ;MIX WITH DATA HIGH LD (HL),A ;PLACE ON SCREEN INC L ;REPEAT UNTIL WIDTH=0 LD DE,128-WIDTH ADD HL,DE EXX DJNZ DEPTH The andtable must start on a page boundary. What this means is the low byte of the address must be zero: E.G. &8000,&8700,&9800 (all in hex) ETC... and will look something like this DB &FF,&F0,&F0,&F0,&F0,&F0,&F0,&F0 DB &F0,&F0,&F0,&F0,&F0,&F0,&F0,&F0 DB &0F,&00,&00,&00,&00,&00,&00,&00 ;REPEAT DB &00,&00,&00,&00,&00,&00,&00,&00 ;*15 3: Mask with data (MIXED METHOD) This method is the best for perfect masking as it enables us to have pen 0 as a sprite colour, But it does double the memory for storing the sprite. You can use the stack pointer with this method like this: LD HL,SCREEN LD SP,DATA LD BC,128-WITDH LD A,DEPTH DEPTH: EX AF,AF' ;REPEAT FOR WITDH COUNT POP DE ;GET MASK AND DATA BYTE LD A,E ;A=MASK AND (HL) ;MASK SCREEN OR D ;MIX DATA LD (HL),A ;REPLACE ON SCREEN INC L ;NEXT SCREEN LOCATION ;REPEAT UNTIL WITDH=0 ADD HL,BC EX AF,AF' DEC A JR NZ,DEPTH ;REPEAT UNTIL DEPTH=0 Or you can do the following method which does not use the stack: LD HL,DATA LD DE,SCREEN LD BC,128-WIDTH LD A,DEPTH DEPTH: EX AF,AF' ;REPEAT FOR WIDTH COUNT LD A,(DE) ;GET SCREEN BYTE AND (HL) ;MASK SCREEN INC L OR (HL) ;MIX WITH DATA INC HL LD (DE),A ;REPLACE ON SCREEN INC E ;REPEAT UNTIL WIDTH=0 EX DE,HL ;SWOP SCREEN WITH DATA ADD HL.BC ;NEXT LINE EX DE,HL ;SWOP SCREEN WITH DATA EX AF,AF' DEC A JR NZ,DEPTH ;UNTIL DEPTH=0 The last method is the most pratical if memory for data allows, And does not use stack, And give 100% perfect mask. **** NOTE **** Through the last section I have stated that it is unwise to use stack. This is only if you wish to have inturpts enables (which the game will have), As when an inturpt occurs it PUSHES onto the stack pointer the current program counter, And if stack points into data then the data will get currupted. (I hope you understood that) Also if you do use stack remmeber to store the stack before you use it and restore when you have finish. E.G: LD (STACK),SP LD SP,WHAT EVER ;DO PRINT OR WAHT EVER LD SP,(STACK) RET STACK: DW 0 Well before I finish the first article a little discusion about Collision Detection. Their are several type that can be used all depend upon speed and accuracy. E.G. Do we need to be pixel perfect (as in check only where the sprite has a pixel colour)- MASK COLISION Or do we only need to check the area of sprite (as in putting an invisable box around it)- BOX COLISION The most common used is BOX colision as this is quick to check and also has a moderate amount of accuracy,Where MASK colision is 100% accurate (if done right) but can be time consuming. MASK COLLISION: The theory behind this is when we mask our sprite mask onto the screen we check if the screen byte has changed. E.G. LD A,(DE) ;get screen byte LD C,A ;store in C AND (HL) ;mask with mask byte CP C ;are they same JR NZ,HIT_PIXEL ;no so hit somthing The only problem is we dont now what we hit, all we now is that a byte after masking does not equal the original byte. So the only games that can use this tecneque are games like Manic Miner where you can only hit things that kill you. BOX COLLISION This is simple,quick and accurate enought to be used in all games except 3D simualtion (but we are not going into that just yet). Basically we get the X and Y coords for one sprite and store them into X1,Y1 , then get the X and Y coords for another sprite and store them into X2,Y2. Now we simply follow the following agarithum: IF X1X2 OR X1>X2 AND X1Y2 OR Y1>Y2 AND Y1