I want to keep the enemies (red rectangles in my case) inside the rectangle.What happens is that since it's a random function , the enemies keep on moving.But slowly some of my enemies moves outside , I don't want this to happen.I want them to be inside the screen only.
I have tried subtracting the screen width and screen height but was unsuccessful in my attempt to do so.So basically what I am asking is how do I check if rectangle is inside the screen.
EDIT :
This is not a possible duplicate as my enemies are moving randomly.In that the player is controlled by the user whereas my program , the movements of the enemy are totally random.
Here is my random movement for my enemies:
def evilMove(evilGuy):
evilCoords = []
#Returns either -1, 0 or 1
randomMovex=random.randrange(-1,2)
randomMovey=random.randrange(-1,2)
evilMake={'x':evilGuy[0]['x']+randomMovex,'y':evilGuy[0]['y']+randomMovey}
del evilGuy[-1] #Delete the previous spawn of enemy so there is no trail
evilCoords.append(evilMake['x']) #Adds x coordinate
evilCoords.append(evilMake['y']) #Adds y coordinate
deadZones.append(evilCoords) #Adds the enemy coordinates in the list
evilGuy.insert(0,evilMake)
Thanks in advance.
Well, you should really look at other questions similar to this, but you could make an if statement where you test if it is outside the screen.
Lets just say your screen is 500 by 500, you would do,
if coordinatesx < 500:
do this and that
if coordinatesy < 500:
do this and that.
This checks if the coordinates of your bad guy are being checked by the screen by saying if the bad guys coordinates are not larger than the screen, then do what you need to do.
EDIT _________________________________________
def evilMove(evilGuy):
evilCoords = []
#Returns either -1, 0 or 1
if x < 500 and y < 500:
randomMovex=random.randrange(-1,2)
randomMovey=random.randrange(-1,2)
evilMake={'x':evilGuy[0]['x']+randomMovex,'y':evilGuy[0]['y']+randomMovey}
Im not sure what your x and y variable is called, just replace it with that. And replace the 500 with your sreen width.
Related
I'm new to python and relatively new to programming in general and I'm trying to write a side scrolling arcade game using the pygame and random modules. However I have hit a stumbling block in the way that the game is populated with enemies. What I am trying to achieve to make it so that for every enemy that leaves the left hand side of the window a new one is spawned somewhere beyond the right hand edge of the window.
However when an enemy leaves the left hand side of the screen and my respawn function is called I get an "typeerror" that the plane object it is trying to add to the enemies list is not callable - I cannot figure out why this is.
To begin with. I have defined a class for each type of enemy in my game. I have tried to get the planes to respawn the way i want them to first and then plan to do the same for the others. so I will only include relevant code to this class of enemies.
class plane(object):
def __init__(self, start_x, start_y, speed):
self.start_x = start_x
self.start_y = start_y
self.speed = speed
self.width = 200
self.height = 60
self.Hitbox = (self.start_x, self.start_y, self.width,
self.height)
def draw(self, win):
pygame.draw.rect(win, (0,0,0), (self.start_x, self.start_y,
self.width, self.height),0)
self.Hitbox = (self.start_x, self.start_y, self.width,
self.height)
pygame.draw.rect(win, (0,255,0), self.Hitbox, 1)
I can create an initial list of enemies by using the following create level function before entering the main loop of the game and i have defined another function called Respawn() It's this Respawn() function that won't work in the way i hoped:
turrets = []
towers = []
planes = []
def createLevel():
for r in range(left_turret_number):
turrets.append(turret(random.randint(1,2651), "Diag_left"))
for r in range(right_turret_number):
turrets.append(turret(random.randint(1,2651), "up"))
for r in range(tower_number):
towers.append(tower(random.randint(150,2651),random.randint(1,450),
50))
for r in range(plane_number):
planes.append(plane(random.randint(500,2651), random.randint(1,
450), random.randint(10, 20)))
def Respawn():
random_plane_x = random.randint(500,2651)
random_plane_y = random.randint(1, 450)
random_plane_speed = random.randint(10, 20)
random_plane = plane(random_plane_x, random_plane_y,
random_plane_speed)
print(random_plane_x, random_plane_y, random_plane_speed)
planes.append(random_plane)
In my main loop the pertinent following things happen in this order:
1.) each plane is moved by their speed towards the left of the window
for plane in planes:
plane.start_x -= plane.speed
2.) each plane is checked to see whether it has completely left the left hand side of the window, and if it has, it is removed from the list and the respawn counter increases by one - I have done it this way in case two planes by chance leave the screen at the same time.
for plane in planes:
plane.start_x -= plane.speed
3.) for the number of respawn counters, the respawn function is called that many times. (this is after collisons have been detected and keyboard input checked for ect.). finally the respawn counter is reset and the game window redrawn.
if plane_respawn_counter > 0:
for r in range(plane_respawn_counter):
Respawn()
plane_respawn_counter = 0
redrawGameWindow()
When a plane leaves the left hand side of the screen and the respawn function is triggered the program simply crashes and I get the error message "TypeError: "plane" object is not callable".
Thank you for your attention - I hope someone can tell me why the object is not callable and hopefully also how I can fix it :) I hope the information I have provided is sufficient - please let me know if you need any more details or need to see any further code from my program.
You should rename your variable in your for loop from planes--planes is the name of your class. Here is where I'm talking about:
for plane in planes:
plane.start_x -= plane.speed
In your respawn function, you call plane,
random_plane = plane(random_plane_x, random_plane_y, random_plane_speed)
but planes has been redefined as an instance of your class plane, therefore your error object not callable. Try just changing your for-loop variable to something else or renaming your class to Plane (it's pretty standard for them to start with an uppercase letter).
So I had to get familiar with turtle for a project with school. I got basically everything my professor asked besides for overlapping the squares.
However he would like the squares to overlap like this
I haven't been able to replicate this at all. I am wondering if their is something I need to throw into my code to easily solve it.
Here is my code
import turtle #Imports the 'turtle module' which allows intricate shapes and pictures to be drawn
my_turtle_pos = (10 , 10)
def square(my_turtle,x,y,length) : #I set up a function that helps me determine the square
my_turtle.penup() #Picks 'up' the turtle pen
my_turtle.setposition(x-length/2,y-length/2) #Helps set positon
my_turtle.pendown() #Puts 'down' the turtle pen
my_turtle.color('black','red') #Allows black outline, with red filling
my_turtle.begin_fill() #Starts the filling of red and helps remember the starting point for a filled area
my_turtle.forward(length) #Moves the turtle by the specified amount 'length'
my_turtle.left(90) #Moves the turtle by given amount '90'
my_turtle.forward(length)
my_turtle.left(90)
my_turtle.forward(length)
my_turtle.left(90)
my_turtle.forward(length)
my_turtle.left(90)
my_turtle.end_fill() #Stops filling with red, which will close with the current color
def graphic_pattern(my_turtle,x,y,length,times): #recursive function
if times <= 0: #This just tells us how many 'times' it needs to repeat till given amount
return
newSize = length/2.2 #This will grab the new size
graphic_pattern(my_turtle,x-length/2,y-length/2,newSize,times-1) #Functions to help with writing 'smaller' squares
graphic_pattern(my_turtle,x-length/2,y+length/2,newSize,times-1)
graphic_pattern(my_turtle,x+length/2,y-length/2,newSize,times-1)
graphic_pattern(my_turtle,x+length/2,y+length/2,newSize,times-1)
square(my_turtle,x,y,length)
my_turtle = turtle.Turtle(shape="arrow") #You can use differen't shapes for the turtle, I chose arrow, though the turtle was cool :)
my_turtle.speed(100) #I am not sure how fast the turtle can go, I just chose 100 cause it went by quicker.
graphic_pattern(my_turtle,3,0,300,4) #Example pattern stated from homework assignment.
I think it's something to do with where the pen draws the squares first. Thanks for any input!
Sounds like it's a simple sequencing of your graphic_pattern() and square() methods. It seems you would want to draw the top right graphic_pattern() first, followed by the middle square, and then followed by the rest of the graphic_pattern() calls:
import turtle #Imports the 'turtle module' which allows intricate shapes and pictures to be drawn
my_turtle_pos = (10 , 10)
def square(my_turtle,x,y,length) : #I set up a function that helps me determine the square
my_turtle.penup() #Picks 'up' the turtle pen
my_turtle.setposition(x-length/2,y-length/2) #Helps set positon
my_turtle.pendown() #Puts 'down' the turtle pen
my_turtle.color('black','red') #Allows black outline, with red filling
my_turtle.begin_fill() #Starts the filling of red and helps remember the starting point for a filled area
my_turtle.forward(length) #Moves the turtle by the specified amount 'length'
my_turtle.left(90) #Moves the turtle by given amount '90'
my_turtle.forward(length)
my_turtle.left(90)
my_turtle.forward(length)
my_turtle.left(90)
my_turtle.forward(length)
my_turtle.left(90)
my_turtle.end_fill() #Stops filling with red, which will close with the current color
def graphic_pattern(my_turtle,x,y,length,times): #recursive function
if times <= 0: #This just tells us how many 'times' it needs to repeat till given amount
return
newSize = length/2.2 #This will grab the new size
graphic_pattern(my_turtle,x+length/2,y+length/2,newSize,times-1)
square(my_turtle,x,y,length)
graphic_pattern(my_turtle,x-length/2,y-length/2,newSize,times-1)
graphic_pattern(my_turtle,x-length/2,y+length/2,newSize,times-1)
graphic_pattern(my_turtle,x+length/2,y-length/2,newSize,times-1)
my_turtle = turtle.Turtle(shape="arrow") #You can use differen't shapes for the turtle, I chose arrow, though the turtle was cool :)
my_turtle.speed(100) #I am not sure how fast the turtle can go, I just chose 100 cause it went by quicker.
graphic_pattern(my_turtle,3,0,300,4) #Example pattern stated from homework assignment.
You have to change the visit order of your recursive calls.
Now the order is post-order (visit all children, then visit the node).
With this particular order (visit upper right child, visit the node, visit the remaining children):
def graphic_pattern(my_turtle,x,y,length,times): #recursive function
if times <= 0: #This just tells us how many 'times' it needs to repeat till given amount
return
newSize = length/2.2 #This will grab the new size
graphic_pattern(my_turtle,x+length/2,y+length/2,newSize,times-1)
square(my_turtle,x,y,length)
graphic_pattern(my_turtle,x-length/2,y-length/2,newSize,times-1) #Functions to help with writing 'smaller' squares
graphic_pattern(my_turtle,x-length/2,y+length/2,newSize,times-1)
graphic_pattern(my_turtle,x+length/2,y-length/2,newSize,times-1)
you can obtain the desired pattern.
Is it possible for me to create a function where it displays a message if the Sprite (Rocket) collides with the astroid objects?
class Rocket(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect=self.image.get_rect()
self.image=Rocket.image
self.firecountdown = 0
def setup(self):
self.rect.x=700
self.rect.y=random.randint(20,380)
def updateposition(self):
self.rect.x=self.rect.x-1
time.sleep(0.005)
if self.rect.x == 0 :
self.rect.x = 700 + random.randint(0, 100)
self.rect.y=random.randint(20,380)
asteroids=[]
asteroidsize=[]
for i in range(25):
x=random.randrange(700,10000)
y=random.randrange(0,400)
asteroids.append([x,y])
asteroids[i]=Asteroid()
for i in range(25):
asteroidsize.append(random.randint(6,15))
while True:
for i in range(len(asteroids)):
pygame.draw.circle(screen,GREY,asteroids[i],asteroidsize[i])
asteroids[i][0]-=2
if asteroids[i][0]<0:
y=random.randrange(0,400)
asteroids[i][1]=y
x=random.randrange(700,720)
asteroids[i][0]=x
You could write a function on your Rocket class that checks for collisions. Since the asteroids are circles, you'll want to check if the closest point on the circle to the center of your sprite's rect is within the rect's bounds:
def check_asteroid_collision( self, asteroid, size ) :
# Create a vector based on the distance between the two points
distx = self.rect.centerx - asteroid[0];
disty = self.rect.centery - asteroid[1];
# Get magnitude (sqrt of x^2 + y^2)
distmag = ((distx * distx) + (disty * disty)) ** 0.5;
# Get the closest point on the circle:
# Circle center + normalized vector * radius
clsx = asteroid[0] + distx / distmag * size;
clsy = asteroid[1] + disty / distmag * size;
# Check if it's within our rect
if self.rect.collidepoint( clsx, clsy ) :
# We're colliding!! Do whatever
print( "Oh no!" );
Then in your game loop, you could check collisions by calling this:
while True:
for i in range(len(asteroids)):
...
# Collision checking
myrocket.check_asteroid_collision( asteroids[i], asteroidsize[i] );
Keep in mind this process is somewhat expensive and it will check every asteroid if it's colliding. If there's a large number of asteroids, it'll run slowly.
While I dont code python I can give you a simple example of how to accomplish something like this.
Make all your game objects inherit from a general game item class, this way you know all items have a position and a collision radius.
class:
int positionX
int positionY
int radius
Then keep all your items in a global list of game objects.
Loop over your game list and see if any two items collide
foreach object1 in gameObjectsList:
foreach object2 in gameObjectsList:
if(object1 != object2)
if(math.sqrt(object1.positionX - object2.positionX)**2 +
(object1.positionY - object2.positionY)**2)
<= object1.radius + object2.radius.
//Things are colliding
As the game progresses make sure you keep the position variables updated in each object.
What this means is basically that you have your list of game objects, and you loop over these every game frame and check if any of them are touching each other.
Or in terms of pure math, use the distance formula (link) to get the distance between the two items, and then check if their combined radius is greater than this distance. If it is they are touching.
Yes, making the score is possible. I am asuming you know the sprite collision function : pygame.sprite.spritecollide(). If you don't, look into the PyGame Docs. or google it. But here is how you do it. First. add these lines of code at the beginning of your code after the pygame,init() line:
variable = 0
variable_font = pygame.font.Font(None, 50)
variable_surf = variable_font.render(str(variable), 1, (0, 0, 0))
variable_pos = [10, 10]
Clearly, variable can be a string, just remove the str() in line 3. Line 1 is self-explanatory - it is the value you will see on the screen (just the stuff after the = sign and/or the parantheses). Line 2 decides what font and size you want the message to be in. If you choose None and 50, it means that you want the message in a normal font and in size 50. The third line renders, or pastes, the message on the screen the name of the variable that contains the string/number, the number 1 (I have no idea why), and the color your message will be. If the variable contains a number, put a str() around it. The last line will be the position of the message. But you will need to blit it first. To prevent the message from appearing on the screen forever, make a variable:
crashed = 0
Then make your instances and groups:
ship = Rocket(None)
asteroids = pygame.sprite.Group() #This is recommended, try making a class for the asteroids
And finally your collision code:
if pygame.sprite.spritecollide(Rocket, asteroids, True):
crashed = 1
You can make your blits controlled with the crashed variable:
if crashed == 0:
screen.blit(ship.image, ship.rect)
elif crashed == 1:
screen.blit(ship.image, ship.rect)
screen.blit(variable_surf, variable_pos)
The last blit line will blit your message on the screen at the location listed (variable_pos) when your ship crashes (crashed = 1 in the collision code). You can use make some code to make crashed back to 0. Remember to do pygame.display.flip() or weird stuff will happen. I hope this answer helps you!
I am currently working on creating a function for my side scroller game in pyhton 3.2.3 and pygame that allows the main character to collect coins. What I am trying to do is when my character's postion is equal to the money's postition, the money disappears. Here is my code:
def drawMoney(x,y):
offset = 0 - guy[X]
guyPos = guy[X]+640
moneyPos = x
print(guyPos,moneyPos)
if lvlNum == level1:
money1 = screen.blit(money,(offset+x, y))
if guyPos == moneyPos or guy[Y] == y:
y = 10000
I am trying to make the money disappear by re-blitting the level picture but for some reason, this does not happen. I am not sure why but for some reason it is not working. Everything seems to be correct but am assuming that the guyPos never equals the moneyPos. Any help is appreciated.
By doing:
offset = 0 - guy[X]
guyPos = guy[X] + offset
you're practically assigning zero to guyPos
For something like this I'd use the colliderect() function already built-in in pygame. Just check, whether the player collides with any of the coins and if so, add the coin to the player's collection by adding the respective amount to that variable and remove the coin from the list of sprites to blit.
In Pseudo-Code:
For coin in coin_list:
Does the coin collide with the player (check by using pygame.Rect.colliderect())
If so: add coin amount to player's coin variable and remove coin from coin_list
To create an abstract, scrolling city skyline for a prototype, I created a class that generates random rectangles. These rects are added to a list and items are pulled from that list to be drawn on the screen. The rects begin off screen to the right and scroll across to the left until they leave the view plane and are trashed. The movement of the buildings is oddly jerky and they also shift to the right a few pixels at a specific point on the screen.
This video of the prototype is fairly accurate with very little video capture lag. Pay attention to the gaps between the buildings, as they get within the right most 3rd of the display area, the gap will suddenly shrink as if the building to the left of the gap is suddenly shifting right a few pixels. The smaller the gap, the more noticeable it is. The other anomalies in the video are from the recorder and are not present in the app. Here's a very short video that clearly shows this phenomenon: https://www.youtube.com/watch?v=0cdhrezjcY8
At about 1 second in you'll notice a very narrow gap between buildings in the rear layer. At :04 seconds in the gap is even with the blue player object, the left rectangle shifts and the gap vanishes. There's a second, larger gap to the right of that one that does the same thing but since the gap is larger it doesn't completely vanish. I've looked over the code numerous times but can't see what could be causing this anomaly. I'm hoping someone can tell me if it's something I did or a limitation I'm encountering.
I originally wrote this program linearly, without classes or functions. I rewrote it using a class that generates layer objects and handles all the generating and scrolling. In both cases the problem exists. It's driving me crazy trying to figure out why the buildings do not move smoothly. I've even written a version of this using png images instead of randomly generated rectangles. In that version the pngs scroll smoothly and seamlessly: https://www.youtube.com/watch?v=Uiw_giAvbOo (The video is a bit jerky, but the actual program plays smooth) So the issue is limited to these random rectangles.
Here's the code for the program: https://www.refheap.com/73079
Here's the class code by itself:
class Scroller():
def __init__(self, speed, color, heightMax):
# Speed of the layer scroll, the color of the layer and the maximum height for buildings
# set up the building parameters
self.buildingHeightMax = heightMax
self.buildingHeightMin = 100
self.buildingWidthMax = 125
self.buildingWidthMin = 75
self.buildings = []
self.layerspeed = speed
self.buildTime = True
self.buildCountdown = 10
self.color = color
def update(self):
# Check if it's time to build. If not, decrement counter
if self.buildTime == False:
self.buildCountdown -= 1
# If time is 0, time to build, reset counter to a new random time
if self.buildCountdown <= 0:
self.buildTime = True
self.buildCountdown = random.randint(3, self.layerspeed)
# create building if it's time
if self.buildTime:
# generate random width and height of building
buildingHeight = random.randint(self.buildingHeightMin, self.buildingHeightMax)
buildingWidth = random.randint(self.buildingWidthMin, self.buildingWidthMax)
buildingTop = WINDOWHEIGHT - buildingHeight
# This generates the building object from the above parameters
building = pygame.Rect(WINDOWWIDTH, buildingTop, buildingWidth, WINDOWHEIGHT)
self.buildTime = False
self.buildCountdown = random.randint(3, self.layerspeed * 5)
# add building to buildings list
self.buildings.append(building)
# move all buildings on layer at set speed
for building in self.buildings:
# if the building is off the screen, trash it. If not, move it to the
# right at the objects speed.
if building.right < 0:
self.buildings.remove(building)
else:
building.left -= self.layerspeed
# draw the Front buildings
for i in range(len(self.buildings)):
pygame.draw.rect(windowSurface, self.color, self.buildings[i])
Your problem most likely lies in:
# move all buildings on layer at set speed
for building in self.buildings:
# if the building is off the screen, trash it. If not, move it to the
# right at the objects speed.
if building.right < 0:
self.buildings.remove(building)
else:
building.left -= self.layerspeed
You're using remove on the same list you're iterating from, and this will make it skip the next building. So it's not the building to the right that's moving faster, it's the one to the left that has skipped moving.
You can see it yourself with this simple example:
a = [2, 3, 4, 1.5, 6, 8, 3.2]
for element in a:
if element == 4:
a.remove(element)
else:
print element
Try it and you'll see that not only 4 won't be printed, but also 1.5 will be skipped.
Possibly a good way to do it is to first iterate through all the buildings to see which ones need to be removed, then remove then all, and finally move all the ones that are left.
You might want to check this link for some good suggestions.
You're also updating the countdown twice, first on line 47 and then on line 58. Is there any reason for this?