Due: , 11:59 pm
This project completes the semester with a design task that requires a multi-stage interaction with the user combined with some type of simulation. This is the fourth and final part of a multi-part project, and the primary challenge is designing and then implementing a more complex user interface and interaction than in prior projects. Alternatively, you can choose to simulate a phenomenon of your choice, such as a spring/mass/damper system, using a design similar to ones we have been using.
If you have not already done so, mount your personal space, create a new project11 folder, and bring up TextWrangler and a Terminal. You will need the Zelle graphics package file graphics.py. You will also want to copy over your physics_objects.py, collision.py, and file containing your rotating block class from project 10 (if it is in a separate file).
The Zelle documentation is also available.
Go ahead and download a simple Ship class. The ship will act like a ball with respect to collisions and motion, but it also has the capability to rotate around its center. Open up the file and go through the various parts to get an understanding of how it works.
In the update() method, the biggest difference from RotatingBlock is the code designed to make the exhaust flicker. This, combined with setFlickerOn() and setFlickerOff(), enable the main loop to turn on flickering for a period of time after the user hits a key.
Test the ship-simple.py file by running it. It doesn't do anything except make a window, render the ship, and wait for a mouse click.
Your first task is to make a main loop. Rather than being sensitive to a mouse click, we want the main loop to terminate only when the user types q. Since we want the program to be sensitive to other keys, we have to avoid using the checkKey() method more than once during our loop, and we can't use it as part of the while condition. Instead, you want to use the following structure.
# assign to a variable key the empty string # while key is not equal to the string 'q' # assign to the variable key the result of win.checkKey() # test to see what the user did (if anything) # run any updates on objects in the scene # close the window and return
To start, just implement the first three lines and the last line of the algorithm above by modifying the main function in ship-simple.py. When you run the file it should close when you hit the q key.
Next, before the main loop, create two variables, dt and frame, and assign them the values 0.01 and 0, respectively. Also, set the ship's rotational velocity to something slow, like 20 (and be sure the ship's acceleration is [0,0]).
In your main loop, at the end, call the ship.update() method, passing in dt as the time step and increment the frame variable. Then, add an if statement specifying that if the frame variable modulo 10 is equal to zero, then call win.update() and call time.sleep() with a value of dt*0.5.
Run your program. The ship should spin slowly and the program should quit when you hit the q key.
The next step is to make the ship rotate when the user hits the right or left arrow keys. If you want to simulate an actual space vehicle, then firing attitude rockets will add or subtract from the current rotational velocity.
If the user hits the left arrow key, you want to add a certain amount of rotational velocity to the ship. If the user hits the right arrow key, you want to subtract a certain amount of rotational velocity from the ship. When the user hits the left arrow, the key variable will have the value 'Left'. When the user hits the right arrow, the key variable will have the value 'Right'.
Before the main loop, create another variable gamma and give it the value 10. Inside your main loop, implement the following code.
# if the user hits the left arrow key # set the rotational velocity to the old rotational velocity plus some amount gamma # call the ship's setFlickerOn method with no arguments # elif the user hits the right arrow key # set the rotational velocity to the old rotational velocity minus some amount gamma # call the ship's setFlickerOn method with no arguments
Run your program. You should be able to rotate the ship left and right. Don't hold the arrow keys down too long.
The next step is to make the ship move forward when the user hits the space bar. This action depends on the current angle of the ship. As with the rotational velocity, each time the user hits the space bar, we're going to add something to the ship's current velocity.
Before the main loop, create another variable delta and give it the value 1. Inside your main loop, add a case for 'space' to the if/elif structure from the prior step.
# elif the user hits the space bar # assign to a the ship's current angle (getAngle) # assign to theta the result of multiplying a by math.pi and dividing by 180 # assign to v the ship's current velocity (getVelocity) # set the ship's velocity to it's new values # The new X velocity is v_new_x = v_old_x + cos(theta) * delta # The new Y velocity is v_new_y = v_old_y + sin(theta) * delta # call the ship's setFlickerOn method with no arguments
Run your program. You should be able to rotate (arrow keys) and accelerate (space bar). How long can you keep the spaceship in the window?
The final task is to make the ship's world wrap from top to bottom and left to right. The basic idea is to figure out if the ship has gone out of bounds. If it has, add or subtract a value from its current position to make it look like the world wraps around the window.
First, create two more variables winWidth and winHeight prior to the loop, to hold the window width and height. Note these are not the size you made the GraphWin--this is world (physics) coordinates, which by default are 10x less than window coordinates. So, if you made your GraphWin 500 x 500, then the height and width of the window in world coordinates is 50 x 50.
Next, implement the following.
# assign to moveit the value False # assign to p the ship's current position. You might want to cast it to a list. # if the x coordinate is less than 0 # add winWidth to the x coordinate # assign to moveit the value True # elif the x coordinate is greater than winWidth # subtract winWidth from the x coordinate # assign to moveit the value True # if the y coordinate is less than 0 # add winHeight to the y coordinate # assign to moveit the value True # elif the y coordinate is greater than winHeight # subtract winHeight from the y coordinate # assign to moveit the value True # if moveit: # set the ship's position to p # set moveit to False
Run your program and see if the ship wraps.
When you are done with the lab exercises, you may start on the rest of the project.
© 2019 Eric Aaron (with contributions from Colby CS colleagues).