Fire Photon Torpedo!
Part of Asteroids is shooting at the asteroids to break them up before they break up your ship. To do this we must implement a class like the Asteroid class. A class is used to describe what an object is and what it does. The "is" part is the data that the object must keep track of. The "does" part is what we need to tell the object to do to make a photon torpedo fly through space.
For a torpedo to fly through space it must know the following things:
- It must know its current location in space: an x,y coordinate.
- It must know which direction it is fired (the heading of the torpedo)
- It must also know the speed and direction of the ship it was fired from (you don't want the torpedo to go slower than the ship it was fired from!)
- The Photon Torpedo must know its lifespan because eventually it must fizzle out (you don't want a torpedo to keep flying forever through space).
So, to create a Photon Torpedo you must provide this information. To do this you might write a statement like this:
bullet = PhotonTorpedo(cv,ship.xcor(),ship.ycor(), \ ship.heading(),ship.getDX(),ship.getDY())
Don't write this code yet. We're not quite ready for changes yet.
To be able to create a Photon Torpedo we need a constructor for our class that takes the information we provided in the statement above and stores it in the object. The variable called self points at the object. To store a value in an object you write
self.variable = value
This stores the value in the object at a place named variable.
Writing a constructor for the class accomplishes the storing of the values in the object. Here is the constructor for the PhotonTorpedo class.
This code should go right after the statements that set screenMaxX, MinX, MaxY, and MinY at the top of the program.
class PhotonTorpedo(RawTurtle): def __init__(self,canvas,x,y,direction,dx,dy): super().__init__(canvas) self.penup() self.goto(x,y) self.setheading(direction) self.color("Green") self.lifespan = 200 self.dx = math.cos(math.radians(direction)) * 2 + dx self.dy = math.sin(math.radians(direction)) * 2 + dy self.shape("bullet")
This takes all the information about a PhotonTorpedo and stores it in the PhotonTorpedo object that self points to. The line that says super().__init__(self) calls the RawTurtle constructor to initialize the RawTurtle part of the object (since a PhotonTorpedo inherits from a RawTurtle it is also a RawTurtle object).
The line that starts self.dx = sets the amount to add to the torpedoes location each time it is moved. This includes the dx value from the ship plus two times the cosine value for the direction the torpedo was fired.
The lifespan variable keeps track of how much longer the torpedo should live.
The code above tells us what a PhotonTorpedo is by storing data in the object. However, there are some things that a PhotonTorpedo must also do. We will need to get some information from the torpedo eventually. We need to know its lifespan and its dx and dy values. We will also need to know how big a PhotonTorpedo is when it collides with something. We'll call this the radius of the torpedo. This means that we need to add some methods to the PhotonTorpedo class.
Here are some methods that must be indented under the PhotonTorpedo class.
def getLifeSpan(self): return self.lifespan def getDX(self): return self.dx def getDY(self): return self.dy
def getRadius(self): return 4
Finally, we need to provide a method to make the PhotonTorpedo move a little bit on the screen so it can be animated by the play function. Photon torpedos move by adding their dx and dy values to the current x and y value of the torpedo. But, each time they move they also decrease their lifespan.
One interesting thing about Asteroids is that the playing field is wrapped. When a torpedo goes off the right side of the field, it reappears on the left side. The same thing happens with all sides of the field. To do this, it is necessary to use modulo arithmetic. Modulo means that each time we add (or subtract) something to the x or y coordinate we mod it by the width of the playing field to wrap it around.
Here is the code for the move method. Again, it must go inside the PhotonTorpedo class.
def move(self): self.lifespan = self.lifespan - 1 screen = self.getscreen() x = self.xcor() y = self.ycor()
x = (self.dx + x - screenMinX) % \ (screenMaxX - screenMinX) + screenMinX y = (self.dy + y - screenMinY) % \ (screenMaxY - screenMinY) + screenMinY self.goto(x,y)
Now that the PhotonTorpedo class is implemented we can start to use it in our code. To use the class we'll need to keep track of a list of all of our torpedoes. We'll call them bullets in our code.
Find the line that sets the asteroids list to the empty list and make an empty list of bullets.
asteroids = [] # This line is already in your program. bullets = [] # Add this line!
Now, to animate the bullets we must write a loop within the play function to move each of the bullets each time play is called. The asteroids are moved the same way. Here is a loop to move the bullets. The one thing that is different is that bullets eventually die.
Put this code inside the play function. It can go right after the def play.
def play(): # This line is already in your program.
deadbullets = []
for bullet in bullets: bullet.move() if bullet.getLifeSpan() <= 0: deadbullets.append(bullet)
When a bullet is dead, it must be removed from the bullet list. To do this, we can add this code right after the code above. This code removes the bullet from the bullet list (called bullets) and then moves the bullet off the screen so it can't be seen anymore. The ht also hides the bullet (which probably doesn't need to be done, since it is off the screen).
Put this code right after the code that you added above.
for bullet in deadbullets: try: bullets.remove(bullet) except: print("didn't find bullet") bullet.goto(-screenMinX*2, -screenMinY*2) bullet.ht()
Finally the only code that is left is to write a little code so when the space bar is pressed it creates a PhotonTorpedo and appends it to the list of bullets. This is very similar to how we got the ship to turn to the right.
Put this code near the end of the program where the other input handlers are written for turning left and right.
def fire(): bullet = PhotonTorpedo(cv,ship.xcor(),ship.ycor(), \ ship.heading(),ship.getDX(),ship.getDY()) bullets.append(bullet) screen.onkeypress(fire," ")
That should be it. Now you should have a ship that fires torpedoes when you press the space bar!!!
Have More Time?
Customize the color or shape of your Photon Torpedoes. You could make them more circular, smaller, or bigger. You might also make the torpedoes shoot faster or live longer.
Give it a try!!!
What's Next?
Next time we'll make the torpedoes blow up asteroids!