ECE 291 Final Project: 3D PacMan

The Team
- Aseet Patel: Background 3D Graphics
- Joseph Yi: Foreground 3D Graphics
- Hashim Electricwala: Sound and Music
- Yue Chang Chang: Main Loop and Ghost Artificial Intelligence
- Derek Fung: 2D Graphics
Introduction
3d PacMan is an 80x86 assembly program that recreates PacMan in a 3d
environment. The player looks through the eyes of PacMan and runs frantically
through a 3d Maze to eat pellets and super-pellets and avoid 4 vicious
ghosts. 3d PacMan exploits the wonders of the 20th century to recreate
PacMan with wonderful .wav audio files and provide textured 3d walls. In
addition Pellets, super-pellets, walls, and ghosts are all represented
in their true size, so distant and orientation are essential to recreating
a real-to-life recreation. Bitmap Scaling becomes vital in this implementation.
Procedure ScaleTank from MP4 was modified for this purpose. The modification
allowed the scaling of any object given the appropriate .PCX file. To complement
the scaled 3d objects, textured 3d walls were provided for the Maze. Practically
any texture map could be used due the excellent universality of the implementation.
Any .PCX file with some minor modifications to its texture map can easily
transform the walls into any design desired. A default "fire"
wall is provided. However, future modifications are made extremely easy
due to this feature.
Meanwhile, PacMan often times gets bored running through the wonderfully
textured world of PacWorld. For this reason, .wav files provide sound effects
for PacMan and the user's listening pleasure. Sound effects are played
when PacMan eats a Pellet, another sound for the eating of Super-Pellets,
and yet another sound for when PacMan gets killed. Implementation of the
sound becomes very challenging when a large number of effects are forced
to be played at a rapid rate. The eating of pellets is one such situation.
To overcome the problem a new approach was taken. All the .wav files were
simultaneously loaded into memory segments, thus enabling faster sound
effects and a great reduction in delay.
Realizing that playing in a solely 3d environment is very difficult,
we include an optional 2d screen besides the 3d view. The user can toggle
on or off the 2d screen using the "tab" key. The 2d screen provides
a quick view to PacMan of where his foes are and where his pellets are.
In addition a Scoreboard and lives-remaining indicator were provided. 3d
PacMan is just the first of many innovative steps taken to improve upon
classic, well-loved arcade games of the past. Hopefully, 3d PacMan will
provide great joy to the future generation of video-game loving children,
and provide a flashback for adults back to the days when they too played
video games. Enjoy!
Implementation
We divided this project into 5 parts: artificial intelligence, background
3D graphics, foreground 3D graphics, 2D graphics and sound. Each part was
handled separately by a team member. The video screen was divided into
2 sections: on the left was a 3D section that showed the maze from PacMan's
perspective, and on the left was a 2D section that displayed the maze.
The 2D maze was included to prove that the ghost AI worked. To play real
3D PacMan, the TAB key could be used to turn the 2D screen on or off.
All our code was written in a modular way. Each of us was responsible
for a single ASM file that would be included in the code segment of the
MAIN.ASM file. Everything was compiled as a single file. Global variables,
constants and PROTO statements were included in separate include files.
Compilation was automated using the NMAKE utility. Each person's code would
then be called from within Main.
The program is controlled via the main loop. Key presses are handled
using our own keyboard interrupt. PacMan's movements are controlled by
the arrow keys via this keyboard interrupt, and the PacManMove procedure
is called from the interrupt handler to update the Maze. Within the main
loop, there is a delay after which the MoveGhosts procedure is called to
move the ghosts. The AI procedures are called from MoveGhosts. Anytime
something changes (eg. either PacMan or the ghosts move,) the screen is
updated.

Description of Main.ASM
The sequence of events in the main loop is:
- Prompt the user to press a key to begin the game
- Put the Video Display into 320x200 graphics mode (13h)
- Run the Introduction
- Set up the program's environment/data files:
- load up the Maze1 data file
- load the PCX files into their data segment
- load the sounds into their segment
- Paint the screen once
- Set up the Keyboard Interrupt
- Enter the main loop, which:
- checks ExitFlag. If ExitFlag is 1, exit, otherwise, check other flags,
to see if:
- PacMan has died, or if
- A sound has to be played, and finally,
- run the DelayTick routine, move each ghost once and update the screen.
- (End Loop)
- To exit the program, DosExit is called. This checks if the keyboard
interrupt has been installed, and de-installs it if so, and then exits
to DOS
Data Representation
Our maze is described in the file Maze1.dta. This is read into the MazeData
variable as an array, and accessed accordingly.
Global variables are used to describe parameters, like PacMan's and
the Ghosts' positions in this maze, what is underneath each ghost (empty,
pellet or super pellet), etc. These global variables are defined in var.inc
(an include file).
AI Procedures - AI.ASM
- InstKeyb
- inputs: none
- outputs: installs the keyboard ISR
- notes: the keyboard ISR replaces the DOS keyboard handler
- written by: Chang, Yue Chang
- KeybISR
- inputs: keyboard interrupt
- outputs: depending on which key is pressed, updates states of PacMan,
Ghosts, etc
- key assignments:
- arrow keys - controls PacMan
- ESC - exits the program
- Tab - turns on 2-D Maze
- S,s - toggles sound
- notes: we only respond to keypresses, keyreleases are ignored
- written by: Chang, Yue Chang
- DeInstKeyb
- inputs: none
- outputs: de-installs the keyboard ISR
- written by: Chang, Yue Chang
- PacManMove
- inputs: Direction - a byte indicating the direction PacMan is trying
to move
- 0= move forward
- 1 = move backward, still facing forward
- 2 = turn left, don't move
- 3 = turn right, don't move
- outputs:
- Checks if the square in Direction is a valid move, then moves. Does
additional processing if PacMan meets a ghost, eats a pellet, or eats a
super pellet.
- notes:
- PacMan cannot move into a ghost. Normally, this would cause PacMan
to die, but here we disable it so that on the next delaytick cycle, the
ghost can eat PacMan
- written by: Chang, Yue Chang
- Random
- inputs: Seed
- outputs: generates a pseudo-random byte-sized number from 0 to 3 and
returns this value in al
- notes:
- Using the Seed, take reciprocal, take fractional part, then take square
root, repeat for each iteration. (2 graphs are attached to show the effectiveness
of this random function.)
- written by: Chang, Yue Chang
- GetOpenCount
- inputs:
- es:[si] = original Ghost position
- es:[di] = original Ghost position
- cx = offset GhostxPosition ds = cs
- outputs:
- es:[di] = new Ghost position
- ax = new direction of Ghost (if 1 or 2 squares are open)
- ax = 4 (if 3 or 4 squares are open)
- ax = 5 (if no squares are open)
- ax = 6 (if PacMan is eaten)
- notes: counts the number of open squares next to Ghost
- 0 open squares: return ax = 5
- 1 open square : move there (back out of dead end)
- 2 open squares: check square where PacMan came from, and move the other
square
- 3 or 4 open squares: return ax = 4
- written by: Chang, Yue Chang
- Track
- inputs:
- es:[si] = original Ghost position
- es:[di] = original Ghost position
- cx = offset GhostxPosition
- ds = cs
- outputs:
- ax = direction 0,1,2,3 to move
- es:[di] = new position in maze
- notes:
- Ghost checks for PacMan within a 10x10 square (ghost in center)
- if PacMan is not near, return with ax = 4
- else find the square that PacMan is nearest to and return
- es:[di] = new position
- ax = 0,1,2,3 as the new direction
- written by: Chang, Yue Chang
- MoveGhost
- inputs:
- GhostNo - 0,1,2,3 indicating the 1st, 2nd, 3rd and 4th ghost
- outputs: moves the 4 ghosts in an intelligent way
- notes:
- calls GetOpenCount to get the number of open squares around the ghost
- depending on the number of open squares, move PacMan accordingly
- if 3 or 4 squares are open, call Track to see if pacman should be tracked
- if Track returns ax = 4, call Random
- if PacMan dies, set PacManDieOn to 1. Further processing will be done
in Main
- written by: Chang, Yue Chang
- NextRound
- inputs: none
- outputs: resets PacMan and Ghosts for the next round leaves the maze
unchanged
- written by: Chang, Yue Chang
2D Graphics Procedures - TwoD.ASM
- Introduction
- Author: Derek
- Purpose: Display the introduction and press SPACE to play.
- Inputs: none
- Ouputs: none
- Ending
- Author: Derek
- Purpose: Display the Ending screen after the game quits, and credit
the authors.
- Inputs: none
- Output: none
- ReadMaze
- Author: Derek
- Purpose: Read the data from a file which contain different level of
maze to the MazeData buffer. (first row contains level1, seond row contains
level2, and so on.)
- Inputs: A filename hold by dx
- Outputs: Load to the MazeData buffer.
- ShowScoreBoard
- Author: Derek
- Purpose: Load PCX files, and display the all the 2D graphics, and call
ShowMaze when DimOn is 1.
- Inputs: Score, Lives, DimOn
- Outputs: display the scoreboard, and lives.
- ShowMaze
- Author: Derek
- Purpose: Display the whole two dimentional maze, ghosts, pacman, pelletes,
and superpelletes, according to the MazeData buffer.
- Inputs: Read the MazeData buffer, SuperPelleteOn
- Outputs: display the 2D-maze by a jump table.
- CopyToBuf
- Author: Derek
- Purpose: A very useful helper procedure to copy a section form a buffer
to another buffer
- Inputs:
- DestSeg - points to the destination segment.
- DestPtr - points to the begining of a destination buffer.
- SourceSeg - points to the source segment.
- SourcePtr - points to the begining
- NumRow - number of rows by pixel want to copy.
- NumCol - number of columb by pixel want to copy.
- Outputs: copy to the destination buffer where es:[di] at.
3D Background Graphics - Backtd.ASM
- DisplayBack3d
- Author: Aseet Patel
- Input: NONE
- Ouput: Calls 3d Maze graphics routines
- Comment: Acts as a bridge between the Game Engine and 3d maze graphics.
The overall game control procedure calls DisplayBack3d to request a refreshment
of the 3d maze screen.
- GetWallData
- Author: Aseet Patel
- Input:
- PacPosition (global)
- MazeData (global)
- Output:
- HallWallData[]
- DistToWall
- Comment: Views hallway which PacMan looks into, and returns information
about the hall in array HallWallData[], and DistToWall. The position and
direction of PacMan are taken into account when formulating the values
in these two variables. Procedure uses 2 Lookup tables (WallDataLookup[]
and NSEWtable1[]).
- DrawWallorOpening
- Author: Aseet Patel
- Input:
- Depth of Hall
- Segment containing PCX image of hall openings
- Byte to start drawing PCX image to (SPtr)
- MapMazeWallRow[]
- MapMazeWallCol[]
- Output:
- Draws piece of hall at the given depth at the screenbuffer.(to eventually
be sent to the 200x320 VGA display.)
- Comment:
- Acts as a helper procedure for DrawFullHallway.
- It takes information stored in MapMazeWallRow and MapMazeWallCol to
find the location to draw hallways and openings. Using this information
it draws the 3d maze.
- DrawEndHallway
- Author: Aseet Patel
- Input:
- DistToWall (global)
- LeftMapMazeWall[]
- LookupEndHall[]
- MapMazeWallCol[]
- Ouput: Draws the end of the hallway in the ScreenBuffer at the given
depth
- Comment:
- Uses Lookup Tables (LookupEndHall[], LeftMapMazeWall[], MapMazeWallCol[])
- Acts as a helper function for subroutine DrawFullHallway.
- Uses the PCX image stored in horizon2.pcx and copies the appropriate
image multiple times to create a background wall.
- DrawFullHallway
- Author: Aseet Patel
- Input:
- DistToWall (global)
- HallWallData[]
- Ouput: Draws Entire 3d hallway into ScreenBuffer ready for VGA display
- Comment:
- Calls multiple helper functions: DrawWallorOpening, DrawEndHallway.
- Uses Data about Hall stored in: HallWallData[].
- When called, DrawFullHallway proceeds to draw the entire hallway (with
the assistance of its helper functions.)
- DrawFullHallway relies on data stored during procedure GetWallData
in variable HallWallData[].
3D Foreground Graphics - Fore3d.ASM
- ViewHallway
- Purpose: Checks the current view of the hallway accrording to the currentdirection
of the PacMan.
- Input: MazeData
- Output:
- ViewObject[12] array according to the depth of the objects. ViewObject
is the current view of the Hallway from PacMan
- Written by Joseph Yi
- DrawObject
- Purpose:
- Finds the closest Ghost and the closest Pellet to PacMan by using the
data from ViewObject byte array.
- If the closest Ghost is detected, calculate its position relative to
Pacman's direction from MazeData[Pacposition]
- Write a ghost and a pellet from Foreground to ScreenBuffer by using
ScaleObject prodedure.
- Before calling ScaleObject procedure need to set the following:
- XPos = 100
- YPos = 100
- ObjectNum(byte): 0=Ghost1, 1=Ghost2, 2=Ghost3, 3=Ghost4, 4=Blue Ghost
- ObjectView(byte): 0=Front, 1=Back, 2=Left, 3=Right Pellet: ObjectNum=0,
ObjectView=4
- SuperPellet: ObjectNum=1, ObjectView=4
- Input:
- PelletYes:
- 0=No Pellet or SuperPellet detected yet
- 1=Pellet already dectected ObjectView: 0=Front, 1=Back, 2=Left, 3=Right
- ObjectNum: 0=Ghost1, 1=Ghost2, 2=Ghost3, 3=Ghost4 4=BlueGhost
- SFactor[0] is the closest to PacMan SFactor[12] is the farthest away
from PacMan
- Output: Writes appropriate images to ScreenBuffer
- Written by Joseph Yi
- MoveScreen
- Purpose: Moves 320x200 pixels of screen data from one segment to another
segment.
- Input: Source Segment Destination Segment
- Output: Writes to Destination Segment
- Written by Joseph Yi
- ScaleObject
- Purpose:
- Draws a scaled object into the ScreenBuffer, centered at the location
specified by XPos, YPos.
- Rotation determines the view of the Object.
- ObjectNum determines which Object is drawn.
- Input:
- XPos
- YPos
- Scalef
- Rotation
- PlayerNum
- Output: Writes to ScreenBuffer
- Written by Derek Fung, modified by Joseph Yi, Modified from MP5 ScaleTank
prodedure
- Show3DObjects
- Input: None
- Output: Draws Objects on the screen
- Purpose:
- Draws 3D objects, ghosts and pellets in their right position and depth
by calling other procedures.
- The view of the objects are from the perspective to the PacMan.
- Calls other procedures
- Written by Joseph Yi
Sound Procedures - Sound.ASM
- LoadSounds
- Author: Hashim Electricwala
- Purpose:
- Load three different Wave files into memory.
- This procedure is called once in the main to load up the wave files
once.
- Description:
- Loads the sound files into memory, and formats the audio data.
- This routine basically Loads different wave files into the allocated
memory block.
- It calls the DOS interrupt 21h to open and closes the file and stores
the 43 byte header into the memory and also loads up the contents of the
file in a segment of 64k segment.
- Inputs: SoundNum
- Outputs: None.
- PlaySound
- Author: Hashim Electricwala
- Purpose:
- Plays a selected digital sound effect from the memory depending upon
the SoundNum that is been passed to PlaySound that represents the name
of the wave file to be played.
- Description:
- This procedure will take an effect number as input depending upon the
situation PacMan is in, and play the corresponding sound effect.
- It will be called every time an event occurs which has a sound attached
to it.
- This is implemented by a jump table that sets the pointer to the appropriate
offset of the file to be played inside the memory since its already loaded
into the memory by the subroutine LoadSounds
- Input: SoundNum
- Output: Sound only
- DMAPlay
- Author: Hashim Electricwala
- Purpose: This routine basically activates the necessay ports of the
DMA controller
- Description:
- It reads and outputs data to the appropriate ports and inputs the lenghth
of the file to 03h and 03hf and calls the WriteDsp routine to write it
to the DSP channel and sets the frequency.
- Inputs: MemLoc that is a 20bit linear address.
- Outputs: Writes data to 220h +12 data port until is done.
- MstrVol
- Author: Hashim Electricwala
- Purpose: Sets the master volume of the Channel 01
- Description: This routine sets the master volume of the volume of channel
- Inputs: AL = 0DDh
- Outputs: Sets the volume, left speaker to high and Right Speaker to
Low.
- ResetDSP
- Author: Hashim Electricwala
- Purpose: To clear the SoundBlaster RESET port 226h
- Description: This routine reads the DATA AVAILABLE bit from port 2
until its high.
- Inputs:
- Length1 - length of the wave file
- Outputs: Writes 0 to SoundBlaster RESET port 226h
- WriteDSP
- Author: Hashim Electricwala
- Purpose: To turn the speakers ON
- Description: This routine writes the data to 220h+12h until the MSB
is cleared in order to turn the speakers ON
- Inputs: AL = 0D1h
- Outputs: Turn the Speakers ON