For our final project, we built a virtual archery game. The game simulates the firing of an arrow on a target without arrows flying around.
The purpose of this project is to attempt building an interactive arcade style game using an Atmega644 microcontroller. In our game, the user navigates the game only via the equipment provided (the bow and arrow). The user has two modes of game play to choose from – 1 or 2 player mode. In 1 player mode, the user is able to play a round of archery by shooting 3 shots. After 3 shots, the results will be summarized. For 2 player mode, the game consists of 3 rounds where each user has 3 shots in each round. The results after each round are summarized and the winner is announced after the final round.
High Level Design
Archery is not a very common sport and it is not conveniently played at home. Parents of young kids are generally reluctant to purchase an archery set since it is potentially dangerous due to the flying arrows. We had an idea that a virtual archery video game would be perfect for youths who are aspiring archers or just for entertainment. The idea is influenced by games such as Wii tennis and virtual golf.
Due to the fact that our black-and-white TV screen is 142 by 199 pixels, we have to draw an ellipse in order to make it look like a circle on the TV screen. The algorithm to draw an ellipse is “ A Fast Bresenham Type Algorithm for Drawing Ellipse” by John Kennedy.
Fast Ellipse Drawing
The main idea in the algorithm is to analyze and manipulate the linear equation so that only integer arithmetic is used in all the calculations.
The equation, represents the equation of an ellipse which is to be plotted using a grid of discrete pixels where each pixel has integer coordinates. There is no loss of generality here as all ellipse with elliptical center not at the origin could be translated into one that has an origin (0,0). We plot the ellipse by comparing errors associated with the x and y coordinates of the points that we are plotting.
In order to avoid divisions, we re-write the above equation in the form .
For a given point P(xi,yi), the quantity is a measure telling where P lies in relation to the true ellipse. If this quantity is negative it means P lies inside the true ellipse, and if this quantity is positive it means P is outside the true ellipse. When this quantity is 0, P is exactly on the ellipse.
We define a function which we call the EllipseError which is an error measure for each plotted point.
In the first quadrant the ellipse tangent line slope is always negative. Starting on the x-axis and wrapping in a counterclockwise direction the slope is large and negative which means the y-coordinates increase faster than the y-coordinates. But once the tangent line slope passes through the value -1 then the x-coordinates start changing faster than the y-coordinates.
Thus we will calculate two sets of points in the first quadrant. The first set starts on the positive x-axis and wraps in a counterclockwise direction until the tangent line slope reaches -1. The second set will start on the positive y-axis and wrap in a clockwise direction until the tangent line slope again reaches the value -1.
For the first set of points we will always increment y and we will test when to decrement x. For the second set of points we will always increment x and decide when to decrement y.
Our test decision as to when to decrease x for the first set of points is based on the comparison of the two values,
When this last inequality holds then we should decrement x when we plot the next point.
We can analyze the tangent line slope by implicitly differentiating the equation.
We will continue to plot points as long as , i.e., tangent slope greater than -1. Four points, corresponding to the four quadrants are plotted at one go.
The video game has two modes: single player and two-player mode. In single player mode, a player fires three shots and gets his/her score after the third shot. The figure below shows the high level design for the game. The IR emitter is positioned on the tip of the arrow while the accelerometer is attached to the end of the arrow. An IR camera will be stationed on top of the TV. The IR emitter and the accelerometer on the arrow will be connected to a 3.3 V source (from a voltage regulator). The IR camera will obtain the position of the IR emitter every two frames. The analog data from the accelerometer is inputted to the Atmega644 via its ADC input.
The major constraint we had is our decision to run the MCU on a 16MHz crystal. This limits the amount of code that could be run during the vertical synch the video since only a limited number of CPU cycles can fit within the vertical synch period. We originally thought about making the game color, but that turned out to be impossible with just our Atmega644 MCU running at 16MHz. Also, the additional hardware like Analog Devices AD724 RGB to NTSC Encoder and ELM 304 NTSC Video Generator may be too much for our budget.
Also, we have to stagger most of the calculations so that they can be carried out in many consecutive vertical synch pulses instead of just one. For instance, as we had to draw 6 ellipses on the screen, we staggered the calculation by drawing one every frame incrementally, resulting in a pleasant animation effect. Therefore, it is more tedious on the software design part to implement a counter to keep track of what section of code to run in which cycle of the vertical synch.
Standards and trademarks
We used the NTSC standard for generating video signal. We generated a non-interlaced, black and white TV signal, which means our game can be played on any standard NTSC CRT TV. Our game didn’t involve any IEEE standards.
Since our infrared devices are used for detection and not communication, we do not have to conform to standards published by the Infrared Data Association (IrDA).
The hardware schematic can be found in the Appendix section.
We used the protoboard of the Atmega644 as our MCU. This was by far the most time consuming aspect of the project due to the large number of components to be soldered. Other major components are the IR camera, accelerometer and the video output circuit.
The IR camera we used was from the Wiimote. It was chosen due to its wide viewing angle (33 degrees horizontally and 23 degrees vertically), its long range (about 20 feet) and the fact that the camera comes with built in image processing. This allows us to obtain a 1024×768 resolution.
The schematic used to connect the camera is based on kako’s schematic. The only difference we did was instead of building a crystal oscillator as shown in the original schematic, we bought a crystal oscillator (XC315-ND) to clock the camera.
The camera communicates via I2C protocol with the MCU. A hot-swappable 2 wire bus buffer (LTC 4301L) is used to isolate the SCL and SDA lines of the camera from the MCU. This is because the camera runs on a 3.3V voltage source while we are powering our MCU at 5V. The pull-up resistors used are obtained from kako’s schematic. In addition we also turn on the internal pull-up resistors in the Atmega644.
The accelerometer used was an ADXL330. It was obtained from a broken Wiimote. It was chosen due to its low cost. The ADXL330, is a 3g, 3-axis accelerometer from Analog Devices. The X, Y, Z output of the accelerometer needs to be connected to a capacitor that is connected to ground. Since we obtained our accelerometer from the Wiimote with the required capacitors soldered in, we were able to read the output directly without additional components.
In our game, we used the Y-axis of the accelerometer as it is the direction of shooting of the arrow. It is connected to pin A0 (ADC pin) of the Atmega644.
The video output circuit is obtained from Lab 3. This is because the functionality of the TV is exactly the same as from Lab 3.
All software code can be found in the Appendix section.
Our code uses the MCU for the following task:
- Initialize and read data from Wiimote IR Camera via I2C interface
- Detect shooting motion using an accelerometer connected to an ADC pin
- Generate video for the game
- Transition between different states of the game
We use the IR camera to read the position of the IR LEDs mounted on the arrow. Before using it, we have to initialize it and adjust its settings. This is done via the I2C protocol using the TWI interface in the Atmega644. The library used for communication via the I2C protocol was taken from last year’s 3D scanner project (i2cmaster.h and twimaster.c).
The camera’s device address and process of initialization was done following instructions in WiiBrew. In writing the initialization of the camera and setting its sensitivity setting, we used Johnny Lee’s code written for PIC18F4550 and kako’s Wii IR camera example code written for the Atmega168. The sensitivity setting we used is the max sensitivity setting suggested by inio on WiiBrew. We found from trial and error that this was best able to detect our IR led accurately.
The IR camera returns X and Y coordinate of the IR led. Each coordinate is encoded in 10 bits where the X coordinate has a range of 0 – 1023 and Y coordinate has a range of 0 – 767. Our screen has a 144×199 resolution. We converted the X, Y coordinate to the coordinates on the screen using the equation below where Ix1 and Iy1 are coordinate obtained from the camera.
x = (Ix1 >> 3) + 5;
y = (Iy1 >> 2) + 5;
This translates into a range of 5 – 132 and 5 – 196 for the X and Y coordinates on the screen. This gives us a sufficient resolution that covers almost our entire screen.
When the camera is unable to detect an IR source, it will output a 0xFFFF value for both X and Y coordinates. Due to overflow, this translates into the coordinates (132, 5) on the screen. Since we draw the point the user is pointing, when the LED is out of range from the camera, it will be seen as a point on the upper right corner of the screen.
We detect shooting by reading the value from the accelerometer on the ADC pin (pin A0). When the arrow on the bow is not released, the value from the accelerometer is at a fairly steady value (between 80 – 85) which is different at each try. We obtain this value during initialization and copied it into the variable thres. When a shot is fired, the value from the accelerometer drops from thres to a value close to 0 and returns back to thres. Thus, we detect a shot has fired if value from ADC < thres/4 and set the shotFlag to 1. We set the shotFlag back to 0 only after we have done all the processing needed in the game play. This prevent the system from being triggered by false positive since after a shot is fired, the value at the accelerometer might oscillate somewhat before returning to its original value. Since the processing time is sufficiently long (about two refreshes on the TV screen), this prevents us from recognizing 1 shot as multiple shots.
The fraction 1/4 used to detect the shot was obtained via trial and error. A large value will cause any small movement to be detected as a “shot” (this will cause unintended shots to be recognized) while a smaller value will require the “shot” to be fired as a sufficient speed (this will be more difficult to use for the user). The value used was to find a balance between these two problems.
The video generation code used is from Lab 3 and video_codeGCC644.c. The ISR used to generate the horizontal and vertical sync and displaying on pixels on the screen is unchanged.
We added three methods to draw and erase on the screen:
- video_erasechar(char x,char y,char c): erase character c at position x,y.
- video_erases(char x,char y,char *str): erase string str starting at position x,y.
- video_putsymbol(char x,char y,char c): draw an 8×8 symbol at index c in symbolbitmap at position x,y.
symbolbitmap is a table of 8×8 symbol stored in program memory. About half of the bitmap used is obtained from the Duckhunt video game project from Spring 2005.
In the video generation code used, we are only able to the screen during the vertical synch (when linecount equals 231). We have 60 line times to do so which translates to about 4ms. This severely limits the amount of processing we could do at each frame.
In each frame, we need do the following processing:
- Read value of accelerometer from ADC. This is executed at every frame.
- Obtain data from IR camera of the position of the LED. This is executed every alternate frame (when frame flag is 0).
- Draw and update screen. This is executed every alternate frame (when frame flag is 1).
Even by alternating between reading data from camera and updating the screen, there is still insufficient time to draw large objects or when transition between states in the game (each state has a different screen requiring erasing everything in previous state and draw new things in the new state when we transition between states). This is done by breaking up the erasing and drawing aspects of each state transition to small steps and updating each step at each new frame. This is controlled using the count variable.
The game play was the most time consuming part in writing our code. Due to the number of states required to be displayed, it was a lot more complex than what we had written for Lab 3. Due to the memory constraints faced, we had to be as efficient as possible software wise. Thus, we had to combine certain states together to be within the memory limit.
|ECE4760 PCB||–||ECE4760 Lab||1||4.00|
|Power Supply||–||ECE4760 Lab||1||5.00|
|B/W TV||–||ECE4760 Lab||1||5.00|
|Small solder board||–||ECE4760 Lab||1||1.00|
|Max233 CPP||–||ECE4760 Lab||1||7.00|
|3.3V Regulator||LP2950-33LPRE3||ECE4760 Lab||1||FREE|
– IR Camera
|Hot Swappable 2-Wire Buffer||LTC4301LCMS8#PBF-ND||Linear Technology||1||SAMPLED|
|Bow & Arrow||–||Amazon.com||1||11.00|
|Alligator clips and jumpers||–||ECE4760 Lab||8||4.00|
For more detail: Virtual Archery Using Atmega644