Why is function to move canvas object not working - python

The objective is to move alien1, atarts from 0,0 then moves all the way to the right, goes down and then all the way to the left, and then down.
from tkinter import *
import random
def enemigos():
global Enemigos #Enemigos downloads the image for alien1
n = random.randint(1,3)
if n == 1:
def movalien1():
alien1 = CanvasJuego.create_image(0,0, anchor = NW, image = Enemigos[0], tags= ('alien1'))
RIGHT1 = True
CoordsAlien1 = CanvasJuego.coords(alien1)
if (CoordsAlien1[0] < 1000 and RIGHT1==True):
CanvasJuego.coords(alien1, CoordsAlien1[0]+5, CoordsAlien1[1])
if ((CoordsAlien1[0]+5)==1000):
RIGHT1 = False
CanvasJuego.coords(alien1, CoordsAlien1[0], CoordsAlien1[1]+50)
elif (CoordsAlien1[0]>0 and RIGHT1==False):
CanvasJuego.coords(alien1, CoordsAlien1[0]-5, CoordsAlien1[1])
if ((CoordsAlien1[0]-5)==0):
RIGHT1 = True
CanvasJuego.coords(alien1, CoordsAlien1[0], CoordsAlien1[1]+50)
def rec():
movalien1()
root.after(20,rec)
root.after(20,movalien1())
Alien1 does appear at (0,0), but it won't move.

The problem is that you create a new "alien" every 20 milliseconds. You should be creating alien1 exactly once outside of movalien1. What is happening is that you create it at 0,0, then move it to 5.0. The alien is at 5,0. The next time through the loop you create a new alien at 0,0, and then move it to 5,0. You keep creating new aliens over and over and moving the new alien to 5,0.
Also, you can use the move method to move an item instead of adjusting its coordinates.
Finally, even though it doesn't actually matter in this code, you are calling after incorrectly here: root.after(20, movealien1()). It needs to be either root.after(20, movealien1) or just directly call movealien1() without using after.

Related

How could I make a dash in a 2D pygame python program?

If the pygame program is just a basic entity you can move normally with arrow keys, how could i make it so that if space is pressed, based on the arrow key that was being held at the moment of pressing, a player dashes slightly to the specified direction? My idea is that when the space is pressed, program checks if other keys are being held down and based on that it rapidly increases the x and/or y coordinate and then stops at specific time, but I don't know how to make it stop as all of this is happening inside a main game loop. Any insight is highly appreciated.
You can use time.perf_counter.
So for using that function you will need to import module time
import time
And now you can store the value of time.perf_counter in a variable when space is pressed.
import time
jumping = False
start_time = 0
while True:
if ("space is pressed"): # Replace condition with what you want
jumping = True
start_time = time.perf_counter()
# Add your code here
if jumping:
# change of x, y and other jumping mechanism code here
if jumping and time.perf_counter() - start_time > 1: # Replace 1 with the amount you want this if condition to trigger, also add value in seconds
jumping = False
I'm not sure what your code is like, but this is how I'd go about it:
def dash(self):
keys = pygame.keys.get_pressed()
if keys[pygame.K_SPACE] and not self.dashing and self.jumping:
# ^^ Can be any key
if self.facing_right:
self.x += 20
if self.facing_left:
self.x -= 20
self.dashing = True
Make sure that when you hit the ground it does self.dashing = False else you will be able to dash forever.
Just put this function in the player class if you are doing it OOP.
Else if you are not using classes, put it anywhere in the file and take out all self..

Pygame: managing collisions when creating 20 randomly placed objects

I'm working on a simple simulation using Pygame. For start I need to create 20 objects and randomly place those along edges of the game window, excluding the top edge. SingleCell class manages objects and defines randomised starting positions for sprites.
This class is then being called in the main simulation class to create 20 sprites and add them to a group:
def _create_cell(self):
"""Create a single sprite and add it to group"""
for cell in range(0,self.settings.cell_count):
c = SingleCell(self)
c_width, c_height = c.rect.size
self.cells.add(c)
This all works fine, but quite a few sprites end up overlapping. In order to fix it after studying docs for pygame.sprite I decided to use pygame.sprite.spritecollideany() in a loop to check whether any of the sprites in a group do collide with one another and move them either horizontally or vertically by width or height, respectively, +1 pixel:
def _check_overlapping_cells(self):
"""Check cells group for collisions based on rect"""
for cell in self.cells:
if pygame.sprite.spritecollideany(cell, self.cells,
collided=None) != 'None':
#If the collision happens along the vertical boundary
#move the sprite down by 1 height +1 pixel
if cell.rect.x == 0 or cell.rect.x == (
self.settings.screen_width - cell.rect.width):
cell.rect.y += (cell.rect.height + 1)
#If the collision along horizontal edge then update x-coord
#by sprite width +1 pixel
elif cell.rect.y == 0:
cell.rect.x += (cell.rect.width + 1)
This worked. Sort of. Some of sprites would still be overlapping others in their new locations. So instead of if I've decided to use while cycle to keep moving them around until there are no more collisions:
def _check_overlapping_cells(self):
"""Check cells group for collisions based on rect"""
for cell in self.cells:
while pygame.sprite.spritecollideany(cell, self.cells,
collided=None) != 'None':
Unfortunately, this causes the sim to enter a seemingly neverending cycle of moving sprites around.
I'm a bit confused as to how to do it properly. Any advice?
EDIT:
I have since tried another approach of trying to check collisions when a sprite is being created by modifying _create_cell method so now it looks like this:
def _create_cell(self):
"""Create a single cell and add it to group"""
for cell in range(0,self.settings.cell_count):
c = SingleCell(self)
c_width, c_height = c.rect.size
if pygame.sprite.spritecollideany(c, self.cells,
collided=None) != 'None':
#If the collision happens along the vertical boundary
#move the sprite up by 1 height +1 pixel
if c.rect.x == 0 or c.rect.x == (
self.settings.screen_width - c.rect.width):
c.rect.y += (-c.rect.height - 1)
self.cells.add(c)
#If the collision along horizontal edge then update x-coord
#by sprite width +1 pixel
elif c.rect.y == (self.settings.screen_height - c.rect.height):
c.rect.x += (c.rect.width + 1)
self.cells.add(c)
elif pygame.sprite.spritecollideany(c, self.cells,
collided=None) == 'None':
self.cells.add(c)
But this way results in fewer than 20 sprites being created and some are still overlapping for some reason.
Perhaps something like this:
def _create_cell(self):
"""Create a single cell and add it to group"""
for cell in range(0,self.settings.cell_count):
c = SingleCell(self)
while spritecollideany(c, self.cells):
c = SingleCell(self)
c_width, c_height = c.rect.size
self.cells.add(c)
Basically, the while loop keeps generating new Cells until it finds one that doesn't collide with any in self.cells. Of course, it that is not possible, then it will loop forever. You could add a counter and abort if it tries too many times.

Get Python turtle window to display graphics and remain open

In this code I can't see why it isn't printing a hexagon 24 times. I tell it to make a 6 sided shape with 60 degrees between lines ( a hexagon) and tell it do turn 15 degrees each time. This ends up being a even 24 for the picture I'm trying to draw.
import turtle
Hex_Count = 0
x = turtle.Turtle()
x.speed(.25)
def Hexagon():
for i in range(24):
for i in range(6):
x.forward(100)
x.left(60)
Hex_Count = Hex_Count + 1
x.left(15)
print(Hex_Count)
Hexagon
But, for some reason, when I run this code the turtle screen pops up for about a half second then closes. How do I get it to perform in the way I want it to?
You have several errors that I corrected for you; I added the explanation in the comments:
import turtle
hexagons_count = 0
my_turtle = turtle.Turtle() # x is not a good name for a Turtle object
# my_turtle.speed(.25) # see #cdlane comment reported in a note under.
def draw_hexagon(): # use explicit names respecting python conventions (no camel case)
global hexagons_count # need global to modify the variable in the function scope
for idx in range(24): # use different dummy variable names in your loops
for jdx in range(6): # use different dummy variable names in your loops
my_turtle.forward(100)
my_turtle.left(60)
hexagons_count += 1
my_turtle.left(15)
print(hexagons_count)
draw_hexagon() # need parenthesis to call the function
turtle.exitonclick() # this to exit cleanly
Note: I know you simply copied it from the OP but my_turtle.speed(.25)
doesn't make sense as the argument should be an int from 0 to 10 or a
string like 'slow', 'fastest', etc. I especially don't understand why
beginners with turtle code that isn't working call turtle.speed() at
all -- it seems to me a function to be tweaked after everything is
working. #cdlane
You have some reference issue, you just need to put the variable hex_count where it needs to be so you don't have error accessing it.
import turtle
x = turtle.Turtle()
x.speed(.25)
def Hexagon():
Hex_Count = 0
for i in range(24):
for i in range(6):
x.forward(100)
x.left(60)
Hex_Count += 1
x.left(15)
print(Hex_Count)
Hexagon()
prints 24
You have several problems with your program. One is that it will when after running through the program, closing the window it created. You can add turtle.exitonclick() to the end of your script which tells python to wait for a click in the graphics window, after which it will exit.
The second problem is that you don't call the Hexagon function because you're missing the parentheses. Even if a function takes no arguments, you still need to call it like:
Hexagon()
The final problem is that you need to define Hex_Count before you try to increment it. Hex_Count + 1 will thrown an error if Hex_Count wasn't already assigned to. You can fix this by putting
Hex_Count = 0
before your for loop in Hexagon.
An approach different in a lot of the details but primarily in its use of circle() to more rapidly draw the hexagons:
from turtle import Turtle, Screen # force object-oriented turtle
hex_count = 0 # global to count all hexagons drawn by all routines
def hexagons(turtle):
global hex_count # needed as this function *changes* hex_count
for _ in range(24): # don't need explicit iteration variable
turtle.circle(100, steps=6) # use circle() to draw hexagons
turtle.left(15) # 24 hexagons offset by 15 degrees = 360
hex_count += 1 # increment global hexagon count
print(hex_count)
screen = Screen()
yertle = Turtle(visible=False) # keep turtle out of the drawing
yertle.speed('fastest') # ask turtle to draw as fast as it can
hexagons(yertle)
screen.exitonclick() # allow dismiss of window by clicking on it

Getting Turtle in Python to recognize click events

I'm trying to make Connect 4 in python, but I can't figure out how to get the coordinates of the screen click so I can use them. Right now, I want to draw the board, then have someone click, draw a dot, then go back to the top of the while loop, wipe the screen and try again. I've tried a couple different options but none have seemed to work for me.
def play_game():
"""
When this function runs, allows the user to play a game of Connect 4
against another person
"""
turn = 1
is_winner = False
while is_winner == False:
# Clears screen
clear()
# Draws empty board
centers = draw_board()
# Decides whose turn it is, change color appropriately
if turn % 2 == 0:
color = RED
else:
color = BLACK
# Gets coordinates of click
penup()
onscreenclick(goto)
dot(HOLE_SIZE, color)
turn += 1
As well intentioned as the other answers are, I don't believe either addresses the actual problem. You've locked out events by introducing an infinite loop in your code:
is_winner = False
while is_winner == False:
You can't do this with turtle graphics -- you set up the event handlers and initialization code but turn control over to the main loop event handler. My following rework show how you might do so:
import turtle
colors = ["red", "black"]
HOLE_SIZE = 2
turn = 0
is_winner = False
def draw_board():
pass
return (0, 0)
def dot(color):
turtle.color(color, color)
turtle.stamp()
def goto(x, y):
global turn, is_winner
# add code to determine if we have a winner
if not is_winner:
# Clears screen
turtle.clear()
turtle.penup()
# Draws empty board
centers = draw_board()
turtle.goto(x, y)
# Decides whose turn it is, change color appropriately
color = colors[turn % 2 == 0]
dot(color)
turn += 1
else:
pass
def start_game():
"""
When this function runs, sets up a new
game of Connect 4 against another person
"""
global turn, is_winner
turn = 1
is_winner = False
turtle.shape("circle")
turtle.shapesize(HOLE_SIZE)
# Gets coordinates of click
turtle.onscreenclick(goto)
start_game()
turtle.mainloop()
Run it and you'll see the desired behavior you described.
I'm assuming that your using Turtle in python(hence the name.)
If that's the case, Here's a link to a helpful post: Turtle in python- Trying to get the turtle to move to the mouse click position and print its coordinates
I know, i know. I hate just link answers as much as the next guy. But The post I gave a link to can probably do a much better job of answering your question than I can.
~Mr.Python
Assuming you're using turtle as mentioned in your title:
>>> import turtle
>>> help(turtle.onscreenclick)
Help on function onscreenclick in module turtle:
onscreenclick(fun, btn=1, add=None)
Bind fun to mouse-click event on canvas.
Arguments:
fun -- a function with two arguments, the coordinates of the
clicked point on the canvas.
num -- the number of the mouse-button, defaults to 1
Example (for a TurtleScreen instance named screen)
>>> onclick(goto)
>>> # Subsequently clicking into the TurtleScreen will
>>> # make the turtle move to the clicked point.
>>> onclick(None)
That means that your callback function, which you have apparently named goto, will take two parameters, an X and Y location.
import turtle
def goto(x, y):
print('Moving to {}, {}'.format(x,y))
turtle.goto(x, y)
turtle.onscreenclick(goto)
turtle.goto(0,0)
Each click that you make will move the turtle to a different position. Note that turtle already has an event loop - you don't need one of your own. Just respond to the clicks.
basically, you need to add an 'x' and 'y' parameter for the onclick and onscreenclick functions. You don't need to use them, they're just dummy params. After filling those out the clicks will work no problem:
window = turtle.Screen()
This function uses the x, y params because i'm saving the clicks in order to specify an area to fill with turtles
def on_left_click_save_coordinates(x, y):
global counter, Fill_COORS1, Fill_COORS2
counter += 1
print(x, y)
if counter == 1:
Fill_COORS1 = (x, y)
elif counter == 2:
Fill_COORS2 = (x, y)
counter = 0
This one doesn't use the x,y params because they are dummies, this one is used to allow multiple options, one of which exits, another tells the turtle to fill in the specified area saved in clicks above.
def on_right_click_open_options(x, y):
global going
last_color = options(window, filler, Fill_COORS1, Fill_COORS2, LAST_BLOCK_USED)
if type(Last_COLOR) == type(bool):
going = True
window.onscreenclick(on_click, btn=1)
window.onscreenclick(open_options, btn=3)
This is an example of a snippet of my code. hope this helps.
btn 3 refers to the right click
btn 1 is the default and isn't necessary to specify and refers to left click
btn 2 is the scroll wheel click, not scroll.
and sorry if this isn't formatted the best, it's my first time posting to stackoverflow. Hope it helps nonetheless

Sprite collide with object function?

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!

Categories