Click here to Skip to main content
14,934,046 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Problem A. (10 points) Simulating gravity and projectile motion
To get you started, we’ll give some pretty detailed instructions for the first few steps of the project: hopefully you can extrapolate from those steps to figure out how to proceed in parts B, C, and D as well.

Our first task is to set up classes to represent the entities that need to be manipulated for the game. One reasonable division of functionality is to have one class for the tennis ball, and another overarching class for the game as a whole (we’ll talk about how to deal with the paddle controls later). We’re going to have the ball class derive from Turtle, since a lot of the functionality we want (a movable image on a screen) is already provided by Turtle.

First, import turtle, math, and random at the top of the file. Then, create a class called Ball to represent our bouncing tennis ball, and have it inherit from the turtle.Turtle class.

The __init__ method of Ball should take the following parameters:
The starting x position for the ball
The starting y position for the ball
The starting x velocity for the ball
The starting y velocity for the ball

The last four values should all be numerical types: integers or floating point numbers. The first thing __init__ should do is call the constructor for its base class (turtle.Turtle). You can do this by just using


since turtle.Turtle’s constructor does not take any arguments other than self.

At this point, you should be able to test your code to make sure that you have properly inherited from turtle.Turtle. For example, if you run the line

>>> Ball(10, 50, 4, 5)

You should get a turtle window with the “ball” (just a turtle for now, we’ll get it more ball-shaped soon) in the center of the screen. The numbers that you put in there won’t matter yet, because you aren’t yet using them for anything.

Next, we want to set up the modified turtle, including a few instance variables that don’t appear in the original turtle.Turtle class. Do the following in Ball.__init__:

Turtles don’t normally have velocity, so add two instance variables, self.vx and self.vy to track x and y velocity of the ball.
This is a ball, not a turtle, so set its shape by calling self.shape('circle')
We also want it the appropriate size, so call self.turtlesize(0.3) Note: If your screen resolution is very high and this is hard to see, feel free to change this number so that it is visible.
Similarly, call the .penup and .setpos methods to initialize your ball objects to not draw and also move to the initial location specified by the constructor’s parameters.

At this point, if you rerun the line from above, you should see the turtle as a circle and also not quite in the middle of the screen.

Next, create a class Game. This class will be responsible for animating the ball and handling input from the player(s). Game should not inherit from any other class. Game’s __init__ method should only take self as a parameter and should do the following:
Call the following functions from the turtle module:
turtle.delay(0) which turns off delays for animation
turtle.tracer(0,0) which tells the turtle not to update its graphics when changes happen - we’ll be doing this manually
turtle.setworldcoordinates(-500, -500, 500, 500) which ensures that no matter what system the code is running on, our window coordinates run from (-500, -500) in the lower left corner to (500, 500) in the upper right corner.
Create a Ball object and store it as an instance variable self.ball. You should pass the following values to its constructor:
The initial x coordinate should be a random float between -100 and 100.
The initial y coordinate should be a random float between 30 and 100
The initial x velocity should be a random float that is in the range from -12 to -6 or in the range from 6 to 12
The initial y velocity should be a random float that is in the range from 4 to 10
After the code that creates that ball, add a call to turtle.update() so that the turtle graphics get drawn with the ball object in the window.

We’ll need to add more to this method later, but for now, put a call to Game()at the bottom of your file (preferably inside of an if __name__ == '__main__' block) and run it. Each time you run the file, you should see the ball appear in a random location above the center of the screen but within a small distance left or right from the center.

Next, we need to add animation. This requires four steps:
Create a move(self) method in the Ball class that will move the Ball to a new position each time it is called, based on its current velocity. This should consist of:
Subtract 0.981 from the y velocity (this represents downward acceleration due to gravity)
Compute the new x position by getting the current x position using the xcor() method and then adding in the ball’s current x velocity
Compute the new y position in a similar way, but with the following difference: If the ball’s new y position would be negative, that means it has struck the ground. Tennis balls bounce, so here is what should happen:
The ball’s new y position should be positive, but only 75% as large as it was.
The ball’s y velocity should be reversed and also 75% as large as it was. (This 75% number comes from a standard tennis ball’s approximate coefficient of restitution, i.e. how much it bounces)
Use the goto/setpos method to move the ball to its new position
Create a method gameloop(self) in Game that does three things:
Calls the move method of the Ball object (self.ball)
Calls the turtle.update() function in order to tell the turtle to update its graphics
Calls itself again 30 milliseconds from now. You can do this using turtle.ontimer(self.gameloop, 30).
At the end of Game.__init__, add the calls
self.gameloop() so that the animation cycle is started immediately, and
turtle.mainloop() so that the turtle window will stay open while the animation runs

If you completed these steps correctly, then you should see the ball start off in a random initial direction, but then accelerate downwards, then bounce when it gets to the midpoint (y=0) of the screen. If you want to be sure the acceleration component is working correctly, temporarily set your initial x and y velocity to 0 and see if the ball accelerates downwards.

Finally, let’s implement the player controls for hitting the tennis ball. We want it so that pressing the space bar will cause you to “hit” the tennis ball, propelling it back up into the air and toward the other side. First create a function called hit(self) in the Ball class. For now, just have this method print out the message “space bar pressed” on the console. Then, in Game.__init__, add the following two lines right before the call to turtle.mainloop():
turtle.onkeypress(self.ball.hit, "space")

These lines cause your hit method to be called every time the space bar is pressed. Test your program to ensure that it’s currently printing “space bar pressed” whenever you press the space bar. You may need to click on the turtle graphics window first to get it to register, and this may fail to work entirely in under some conditions.

Assuming that worked, alter your hit function to do the following:
Reverse the ball’s x velocity
Set the ball’s y velocity to 15

You should now be able to “hit” the ball back and forth across the screen by pressing the space bar multiple times. A real tennis player would want a bit more control over how they hit the ball, and we will get there.

The instructions for the remaining parts of the assignment are intentionally left somewhat vague, so that you have a chance to decide for yourself how best to design the game. We will grade only based on the requirements stated, so if something is not specified then it is up to you how to implement it: we will only grade based on whether you fulfilled the requirements stated.

If you’re not clear on what the actual requirements are for any of the below, then feel free to ask clarifying questions to the course staff.

Problem B. (2 points) Net and court
Have Game’s __init__ also draw a net (a line that is 30 units tall) and a court (a line from (-400, 0) to (400, 0))

Problem C. (8 points) Collisions and restart
If the ball hits the net, or if it bounces twice on the same side, then the point is over and the ball should reset. Add code to the gameloop method that can detect if the ball hits the net. If this happens, have the ball reset to a new random initial position and velocity. You may want to create a separate Ball method for this, to avoid having redundant reset code in multiple places.

Note: Actually checking whether the ball hits the net requires a bit of math, but you can approximate this each step by checking whether the ball passes over the x = 0 line, and ends at a y-position below 30.

If the ball bounces twice on the same side of the net, then the point is over and the ball should reset. Keep track of the number of times a ball bounces inside of the ball’s move method, but make sure to reset this value to 0 every time the ball crosses from one side of the net to the other.

Once you’ve implemented these two, the game will get significantly harder! Feel free to tweak the values in the initialization and in the hit method to make it slightly easier to play.

What I have tried:

import turtle
import random
import math
class Ball(turtle.Turtle):
    def __init__(self, px, py, vx, vy):
        self.setpos(px, py)
        self.vx = vx
        self.vy = vy
    def move(self):
        self.vy = self.vy - 0.981
        new_px = self.xcor() + self.vx
        new_py = self.ycor() + self.vy
        if new_py < 0:
            new_py = -0.75 * new_py
            self.vy = -0.75 * self.vy
        self.setpos(new_px, new_py)
        px = new_px
        py = new_py
    def hit(self):
        self.vx = -1 * vx
        self.vy = vy + 15
    def reset(self):
        px = random.uniform(-100, 100)
        py = random.uniform(30, 100)
        vx = random.choice([random.uniform(-12, -6),  random.uniform(6, 12)])
        vy = random.uniform(4, 10)

class Game:
    def __init__(self):
        turtle.setworldcoordinates(-500, -500, 500, 500)
        px = random.uniform(-100, 100)
        py = random.uniform(30, 100)
        vx = random.choice([random.uniform(-12, -6),  random.uniform(6, 12)])
        vy = random.uniform(4, 10)
        self.player = Ball(px, py, vx, vy)
        turtle.onkeypress(self.player.hit, "space")
    def gameloop(self): 
        turtle.ontimer(self.gameloop, 30)

Updated 30-Apr-21 23:45pm
Richard MacCutchan 1-May-21 5:45am
You need to provide more details. We do not know exactly what you are stuck on.
Member 15178929 1-May-21 6:58am
I have the following error in problem 1:
invalid command name "1576591338560gameloop"
while executing
("after" script)
And I do not know what to do with problem 3
Thank you!
Richard MacCutchan 1-May-21 8:06am
1. Where does the invalid command name error occur?

2. And I do not know what to do with problem 3: You follow the instructions in the notes. The first thing is to figure out how to measure when the ball hits. This is usually when it hits some point along a vertical or horizontal line (depending on where the 'net' is assumed to be positioned).
Member 15178929 1-May-21 9:35am
it occurs after turtle runs, and it doesn't say which line it occurs. I don't even know what it means
Richard MacCutchan 1-May-21 9:59am
Sorry, that does not offer any clues.
Member 15178929 1-May-21 10:01am
Doesn't matter. Thank you!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900