Related
I am confused on how I add gravity to my platformer game. I am using pygame for this project. I am relatively new to coding, but anyways here's my code.
import pygame
pygame.init()
win = pygame.display.set_mode((800, 500))
pygame.display.set_caption("Platformer")
x = 200
y = 200
width = 20
height = 20
vel = 5
run = True
FPS = 30
fpsClock = pygame.time.Clock()
while run:
pygame.time.delay(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > 0:
x -= vel
if keys[pygame.K_d] and x < 800 - width:
x += vel
if keys[pygame.K_w] and y > 0:
y -= vel
if keys[pygame.K_s] and y < 500 - height:
y += vel
win.fill((0, 0, 0))
pygame.draw.rect(win, (255, 0, 0), (x, y, width, height))
pygame.display.update()
pygame.display.update()
fpsClock.tick(FPS)
I hope someone can help me get some sort of gravity function working, anything helps. (also what is the best way to make pixelated art, not very relevant to the question)
Gravity just means that the object is moved down a little in each frame:
gravity = 3
while run:
# [...]
y += gravity
if y > win.get_height() - 20:
y = win.get_height() - 20
I suggest to use a pygame.Rect object to represent the player:
import pygame
pygame.init()
win = pygame.display.set_mode((800, 500))
pygame.display.set_caption("Platformer")
player = pygame.Rect(200, 200, 20, 20)
vel = 5
gravity = 3
run = True
FPS = 30
fpsClock = pygame.time.Clock()
while run:
fpsClock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
player.x += (keys[pygame.K_d] - keys[pygame.K_a]) * vel
if player.left < 0:
player.left = 0
if player.right > win.get_width():
player.right = win.get_width()
player.y += gravity
if player.bottom > win.get_height():
player.bottom = win.get_height()
win.fill((0, 0, 0))
pygame.draw.rect(win, (255, 0, 0), player)
pygame.display.update()
pygame.quit()
I don't know how to turn this code into a class statement and then run it!
I want to have the same result as this but using class statements.
import pygame
import random
pygame.init()
winh = 500
winl = 500
win = pygame.display.set_mode((winh, winl))
width = 20
vel = 5
y = 250
x = 250
score = 0
direction = "up"
class Dot():
def __init__(self):
self.x = x
self.y = y
self.width = width
self.direction = 'right'
def direction(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.direction = "up"
if keys[pygame.K_a]:
self.direction = "left"
if keys[pygame.K_s]:
self.direction = "down"
if keys[pygame.K_d]:
self.direction = "right"
p = True
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
direction = "up"
if keys[pygame.K_a]:
direction = "left"
if keys[pygame.K_s]:
direction = "down"
if keys[pygame.K_d]:
direction = "right"
if direction == "up":
y -= width
if direction == "down":
y += width
if direction == "left":
x -= width
if direction == "right":
x += width
if y > winh - 20:
y = 20
if y < 20:
y = winh - 20
if x > winl - 20:
x = 20
if x < 0:
x = winl - 20
win.fill((0,0,0))
dot = pygame.draw.rect(win, (0, 255, 0), (x, y, width, width))
pygame.display.update()
pygame.quit()
So you're class was getting there already. A class is an encapsulation of data and functions that are common to that "object". Anything for or about that object should go into this class. But when code for keyboard handling was put into a Dot class, this was going in the wrong direction. A Dot is a thing draw to a screen, it has a size and colour, and position. It should not be responsible for handling user-input. That's outside the scope of what a Dot is.
When updating the class, I chose to base it on the PyGame sprite class. This is a little bit more work at first, and it needs to be written in a particular way - have an image, and a rect, and (hopefully) an update() function. But being a sprite it gets lots of functionality that's already written, including the PyGame collision routines.
Near the main loop, there's a couple of additions. The first is creating the sprite dotty, and a sprite-group sprites to hold it. It's a bit weird to create a sprite group for a single sprite, but I assumed in the future there would be more than one.
Then in the actual loop, the code calls sprites.update(). This is a handy routine that calls the update() function for every sprite in the group. Then later on, there's a call to sprites.draw( win ) which paints every sprite in the group to the screen. This uses the sprite's image and the rect to know where, and what to paint.
import pygame
import random
pygame.init()
WIN_H = 500
WIN_L = 500
win = pygame.display.set_mode((WIN_H, WIN_L))
width = 20
vel = 5
y = 250
x = 250
score = 0
direction = "up"
class Dot( pygame.sprite.Sprite ):
def __init__( self, x,y, size=20, direction='right', colour=(0,255,0) ):
pygame.sprite.Sprite.__init__( self )
self.size = size
self.colour = colour
self.direction = direction
self.image = pygame.Surface( ( size, size ), pygame.SRCALPHA )
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.image.fill( colour )
def move( self, x_dir, y_dir ):
global WIN_H, WIN_L
# Adjust our co-oridinates
self.rect.x += x_dir
self.rect.y += y_dir
# Stay on the screen, and wrap around
if (self.rect.left >= WIN_L ):
self.rect.right = 0
elif (self.rect.right <= 0 ):
self.rect.left = WIN_L
if (self.rect.top >= WIN_H ):
self.rect.bottom = 0
elif (self.rect.bottom <= 0):
self.rect.top = WIN_H
def update( self ):
# TODO - handle animation, collisions, whatever
pass
# make some sprites
dotty = Dot( x,y )
sprites = pygame.sprite.Group() # a group, for a single sprite (or more)
sprites.add( dotty )
p = True
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
direction = "up"
if keys[pygame.K_a]:
direction = "left"
if keys[pygame.K_s]:
direction = "down"
if keys[pygame.K_d]:
direction = "right"
if direction == "up":
dotty.move( 0, -width )
if direction == "down":
dotty.move( 0, width )
if direction == "left":
dotty.move( -width, 0 )
if direction == "right":
dotty.move( width, 0 )
# update all the sprites
sprites.update()
# repaint the screen
win.fill((0,0,0))
sprites.draw( win )
#dot = pygame.draw.rect(win, (0, 255, 0), (x, y, width, width))
pygame.display.update()
pygame.quit()
The movement code was moved into the Dot class. This allows the Dot to adjust its position, but also take care of screen-wrapping issues. If this sprite was say, some kind of projectile, maybe when it crossed the screen boundary it would call the sprite.kill() function to remove itself from the sprite group. But since a Dot is clearly not a projectile, it can warp to the other side.
This is a project that I have been working on for a while.
Here's the situation:
The code completes the end goal. It's meant to simulate the location of a ball suspended in air by four cables, and should be controlled by a simple gamepad. The code does this, so it accomplishes the goal in the most basic definition.
Here's the problem:
The ball only moves in one point when the "LT" button is pressed, which it is supposed to do. However, after this one movement, the ball doesn't move until the button is pressed again, even if the "LT" button is held. The same thing applies for the "RT" button to move the ball away.
(Although the joystick's trackball doesn't exactly act like the inward-outward problem, I consider this to be the same problem.)
As I said, the most basic definition of this program's goal has been accomplished. However, the true spirit of the goal is coming up just short.
Here's my question:
How should I get the ball to move as long as the "LT/RT" buttons are pressed and/or the trackball is being moved?
The code:
#Import modules
from tkinter import *
import pygame
#Initialize joystick
pygame.joystick.init()
if pygame.joystick.get_count() == 0:
print('No joystick detected')
else:
joystick = pygame.joystick.Joystick(0)
joystick.init()
###
# Interpret Joystick inputs
###
class Find_Joystick:
def __init__(self, root):
self.root = root
## initialize pygame and joystick
pygame.init()
if(pygame.joystick.get_count() < 1):
# no joysticks found
print("Please connect a joystick.\n")
self.quit()
else:
# create a new joystick object from
# ---the first joystick in the list of joysticks
joystick = pygame.joystick.Joystick(0)
# tell pygame to record joystick events
joystick.init()
## bind the event I'm defining to a callback function
## start looking for events
self.root.after(0, self.find_events)
def find_events(self):
## check everything in the queue of pygame events
events = pygame.event.get()
joystick = pygame.joystick.Joystick(0)
axisX = joystick.get_axis(2)
axisY = joystick.get_axis(3)
LT = joystick.get_button(6)
RT = joystick.get_button(7)
for event in events:
# event type for pressing any of the joystick buttons down
if event.type == pygame.JOYBUTTONDOWN:
if LT == 1:
self.root.event_generate('<<LT>>')
if RT == 1:
self.root.event_generate('<<RT>>')
if event.type == pygame.JOYAXISMOTION:
#Move left
if axisX < 0:
self.root.event_generate('<<Left>>')
#Move right
if axisX > 0:
self.root.event_generate('<<Right>>')
#Move upwards
if axisY < -0.008:
self.root.event_generate('<<Up>>')
#Move downwards
if axisY > -0.008:
self.root.event_generate('<<Down>>')
## return to check for more events in a moment
self.root.after(20, self.find_events)
def main():
#################
### Setup Window
#################
###
#Build All Components
###
#Window 1
root = Tk()
app = Find_Joystick(root)
frame = Canvas(root, width=1000, height = 1000)
frame.pack()
#Ball
global ball
global ballBox1
global ballBox2
ballBox1 = [495, 245]
ballBox2 = [505, 255]
ball = frame.create_oval(ballBox1, ballBox2, outline = 'red', fill = 'red')
ballCenter = [500, 250]
ballCoords = [50, 50, 50]
global ballLine1
global ballLine2
global ballLine3
global ballLine4
ballLine1 = frame.create_line((300, 150), ballCenter, fill = 'red')
ballLine2 = frame.create_line((400, 100), ballCenter, fill = 'red')
ballLine3 = frame.create_line((600, 150), ballCenter, fill = 'red')
ballLine4 = frame.create_line((700, 100), ballCenter, fill = 'red')
ballLines = (ballLine1, ballLine2, ballLine3, ballLine4)
#Cube
cubeFront = frame.create_rectangle([300, 150], [600, 400], width = 4.0, outline = 'blue') #Front Face
cubeBack = frame.create_rectangle([400, 100], [700, 350], outline = 'blue') #Back Face
leftEdgeTop = frame.create_line([400, 100], [300, 150], width = 4.0, fill = 'blue') #Top Left Edge
leftEdgeBottom = frame.create_line([300, 400], [400, 350], fill = 'blue') #Bottom Left Edge
rightEdgeTop = frame.create_line([700, 100], [600, 150], width = 4.0, fill = 'blue') #Top Right Edge
rightEdgeBottom = frame.create_line([600, 400], [700, 350], width = 4.0, fill = 'blue') #Bottom Right Edge
backEdgeTop = frame.create_line([400,100],[700,100], width = 4.0, fill = 'blue') #Top Back Edge
backEdgeRight = frame.create_line([700, 100], [700, 350], width = 4.0, fill = 'blue') #Right Back Edge
###
# Position Chart
###
#Box
positionBox = frame.create_rectangle([450, 475], [550, 550])
positionBoxLine1 = frame.create_line([500, 475], [500, 550])
positionBoxLine2 = frame.create_line([450, 500], [550, 500])
positionBoxLine3 = frame.create_line([450, 525], [550, 525])
positionBoxTextX = frame.create_text([475, 487.5], font = ('Arial', 14), text = 'X')
positionBoxTextY = frame.create_text([475, 512.5], font = ('Arial', 14), text = 'Y')
positionBoxTextZ = frame.create_text([475, 537.5], font = ('Arial', 14), text = 'Z')
global positionBoxCoordsX
global positionBoxCoordsY
global positionBoxCoordsZ
positionBoxCoordsX = frame.create_text([525, 487.5], font = ('Arial', 14), text = ballCoords[0])
positionBoxCoordsY = frame.create_text([525, 512.5], font = ('Arial', 14), text = ballCoords[1])
positionBoxCoordsZ = frame.create_text([525, 537.5], font = ('Arial', 14), text = ballCoords[2])
positionCoords = [positionBoxCoordsX, positionBoxCoordsY, positionBoxCoordsZ]
##################
### Move the Ball
##################
###
# Redrawing Function
###
#Redraw the object for it's position
def redrawObjects():
global ballLine1
global ballLine2
global ballLine3
global ballLine4
global positionBoxCoordsX
global positionBoxCoordsY
global positionBoxCoordsZ
global ball
frame.delete(ballLine1)
frame.delete(ballLine2)
frame.delete(ballLine3)
frame.delete(ballLine4)
frame.delete(ball)
frame.delete(positionBoxCoordsX)
frame.delete(positionBoxCoordsY)
frame.delete(positionBoxCoordsZ)
ballLine1 = frame.create_line((300, 150), ballCenter, fill = 'red')
ballLine2 = frame.create_line((400, 100), ballCenter, fill = 'red')
ballLine3 = frame.create_line((600, 150), ballCenter, fill = 'red')
ballLine4 = frame.create_line((700, 100), ballCenter, fill = 'red')
ball = frame.create_oval(ballBox1, ballBox2, outline = 'red', fill = 'red')
positionBoxCoordsX = frame.create_text([525, 487.5], font = ('Arial', 14), text = ballCoords[0])
positionBoxCoordsY = frame.create_text([525, 512.5], font = ('Arial', 14), text = ballCoords[1])
positionBoxCoordsZ = frame.create_text([525, 537.5], font = ('Arial', 14), text = ballCoords[2])
###
# Define each movement function
###
#Indicate that the window is selected
def callback(event):
frame.focus_set()
#Ball moves left
def moveLeftFunc(event):
if ballCoords[0] > 0:
global ballBox1
global ballBox2
ballBox1[0] -= 3
ballBox2[0] -= 3
ballCenter[0] -= 3
ballCoords[0] -= 1
redrawObjects()
#Ball moves right
def moveRightFunc(event):
if ballCoords[0] < 100:
global ballBox1
global ballBox2
ballBox1[0] += 3
ballBox2[0] += 3
ballCenter[0] += 3
ballCoords[0] += 1
redrawObjects()
#Ball moves up
def moveUpFunc(event):
if ballCoords[1] < 100:
global ballBox1
global ballBox2
ballBox1[1] -= 2.5
ballBox2[1] -= 2.5
ballCenter[1] -= 2.5
ballCoords[1] += 1
redrawObjects()
#Ball moves down
def moveDownFunc(event):
if ballCoords[1] > 0:
global ballBox1
global ballBox2
ballBox1[1] += 2.5
ballBox2[1] += 2.5
ballCenter[1] += 2.5
ballCoords[1] -= 1
redrawObjects()
#Ball moves inward
def moveInFunc(event):
if ballCoords[2] > 0:
global ballBox1
global ballBox2
ballBox1[0] -= 1
ballBox1[1] += 0.5
ballBox2[0] -= 1
ballBox2[1] += 0.5
ballCenter[0] -= 1
ballCenter[1] += 0.5
ballCoords[2] -= 1
redrawObjects()
#Ball moves outwards
def moveOutFunc(event):
if ballCoords[2] < 100:
global ballBox1
global ballBox2
ballBox1[0] += 1
ballBox2[0] += 1
ballBox1[1] -= 0.5
ballBox2[1] -= 0.5
ballCenter[0] += 1
ballCenter[1] -= 0.5
ballCoords[2] += 1
redrawObjects()
###
# Bind keys to movement
###
#Initiate movement when window is clicked
frame.bind("<Button-1>", callback)
#Move ball left with "Left Arrow" Key
frame.bind('<Left>', moveLeftFunc)
#Move ball right with "Right Arrow" Key
frame.bind('<Right>', moveRightFunc)
#Move ball up with "Up Arrow" Key
frame.bind('<Up>', moveUpFunc)
#Move ball down with "Down Arrow" Key
frame.bind('<Down>', moveDownFunc)
#Move ball inwards with "Tab" Key
frame.bind('<Tab>', moveInFunc)
#Move ball outwards with "Return" Key
frame.bind("<\>", moveOutFunc)
#############
#I think this is where the problem is
#############
def find_events():
## check everything in the queue of pygame events
events = pygame.event.get()
joystick = pygame.joystick.Joystick(0)
axisX = joystick.get_axis(2)
axisY = joystick.get_axis(3)
LT = joystick.get_button(6)
RT = joystick.get_button(7)
for event in events:
# event type for pressing any of the joystick buttons down
if event.type == pygame.JOYBUTTONDOWN:
if LT == 1:
global ballBox1
global ballBox2
ballBox1[0] += 1
ballBox2[0] += 1
ballBox1[1] -= 0.5
ballBox2[1] -= 0.5
ballCenter[0] += 1
ballCenter[1] -= 0.5
ballCoords[2] += 1
if RT == 1:
#root.event_generate('<<RT>>')
global ballBox1
global ballBox2
ballBox1[0] -= 1
ballBox1[1] += 0.5
ballBox2[0] -= 1
ballBox2[1] += 0.5
ballCenter[0] -= 1
ballCenter[1] += 0.5
ballCoords[2] -= 1
if event.type == pygame.JOYAXISMOTION:
#Move left
if axisX < 0:
#root.event_generate('<<Left>>')
global ballBox1
global ballBox2
ballBox1[0] -= 3
ballBox2[0] -= 3
ballCenter[0] -= 3
ballCoords[0] -= 1
#Move right
if axisX > 0:
#root.event_generate('<<Right>>')
global ballBox1
global ballBox2
ballBox1[0] += 3
ballBox2[0] += 3
ballCenter[0] += 3
ballCoords[0] += 1
#Move upwards
if axisY < -0.008:
#root.event_generate('<<Up>>')
global ballBox1
global ballBox2
ballBox1[0] -= 1
ballBox1[1] += 0.5
ballBox2[0] -= 1
ballBox2[1] += 0.5
ballCenter[0] -= 1
ballCenter[1] += 0.5
ballCoords[2] -= 1
#Move downwards
if axisY > -0.008:
#root.event_generate('<<Down>>')
global ballBox1
global ballBox2
ballBox1[1] += 2.5
ballBox2[1] += 2.5
ballCenter[1] += 2.5
ballCoords[1] -= 1
#Move In
root.bind('<<LT>>', moveInFunc)
#Move Out
root.bind('<<RT>>', moveOutFunc)
#Move Left
root.bind('<<Left>>', moveLeftFunc)
#Move Right
root.bind('<<Right>>', moveRightFunc)
#Move Up
root.bind('<<Up>>', moveUpFunc)
#Move Down
root.bind('<<Down>>', moveDownFunc)
#Main loop
root.mainloop()
if __name__ == '__main__':
main()
Sorry for the really long code. I highlighted the area that I think is where the problem is, but I included the entire code because (as most code is) the entire program is heavily interconnected, and I wanted you guys to be able to test the program yourselves.
Interesting demo! Looking through your code on a quick first pass, its organization is a bit confusing. In particular, there appear to be two find_events() functions where you appear to loop through pygame events. It might not hurt to do some refactoring to clean this and some of those pesky global variables up.
However, code judgement aside, I think you are right about where the problem is for your joystick input is. Let's take a look at the relevant portion of your code:
def find_events():
## check everything in the queue of pygame events
events = pygame.event.get()
#bunch of joystick button-fetching (skipping this)
for event in events:
# event type for pressing any of the joystick buttons down
if event.type == pygame.JOYBUTTONDOWN:
pass#lots of stuff I am cutting out here
if event.type == pygame.JOYAXISMOTION:
pass#lots of stuff I am cutting out here as well :)
The fundamental issue is that you are only making changes to your simulation when the pygame events initially fire. Even though you presumably call find_events() every frame and update the pygame queue, pygame will only add events to the queue when something changes. This explains why you need to keep repeatedly pressing those joystick buttons over and over again; each time you press the button, you add a pygame.JOYBUTTONDOWN event to the queue. The result is that your button press only lasts a single frame.
So what is the solution? We need to keep track of the current state of each of these buttons. There are actually two ways you could go about doing this:
Track and update the state of each button after looping through the pygame.event.get() queue in your own dictionary and use this dictionary to control input in your simulation.
Ignore the whole pygame.event.get() queue and use the get_button() function that pygame provides in its joystick class to get the current state of the button, regardless of whether or not you pressed it or released it in a given frame.
I will just focus on option 1, since with option 2, it is possible to miss user input (e.g. think what could happen if you only check once a frame, and a user has ninja-like reflexes and presses and releases the button quickly within that frame?). This is not so much an issue for joystick input, but rather for keyboard input where you could miss typed letters, but the same idea applies. With that preamble out of the way, let's look at some code:
def find_events():
#your dictionary for updating button states (pressed == True)
#also, ew, globals!
global joystick_button_state
events = pygame.event.get()
joystick = pygame.joystick.Joystick(0)
axisX = joystick.get_axis(2)
axisY = joystick.get_axis(3)
LT = joystick.get_button(6)
RT = joystick.get_button(7)
buttons = [joystick, axisX, axisY, LT, RT]#ideally, do NOT create a list of buttons every time you call this!
for event in events:
if event.type == pygame.JOYBUTTONDOWN:
for button in buttons:
if event.button == button:
joystick_button_state[button] = True
elif event.type == pygame.JOYBUTTONUP:
for button in buttons:
if event.button = button:
joystick_button_state[button] = False
Once you do that, now all you have to do is look at joystick_button_state[the_one_button_i_care_about] and see if it is True/False to see if it is pressed or not. This should work nicely for basic buttons, and you can store events from pygame.JOYAXISMOTION in a similar way (perhaps another dict?). Hope this explanation clears up your issue!
I want to create a pygame window that doesn't have a frame and that moves when the user clicks on it and moves the mouse.
I tried this script but when I click on the windows, '0' is printed but not '1'
Something is wrong in my script.
# coding : utf-8
import pygame
from pygame.locals import *
from random import randint
from os import environ
from math import sqrt
pygame.init()
max_fps = 250
clock = pygame.time.Clock()
window_size_x, window_size_x = 720, 360
infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w / 2)) + ',' + str(int(infos.current_h / 2)) # center the window
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)
def move_window(): # move the windows when custom bar is hold
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
mouse_x, mouse_y = pygame.mouse.get_pos()
dist_x , dist_y = mouse_x - window_x, mouse_y - window_y # calculate the distance between mouse and window origin
for event in pygame.event.get():
if event.type != MOUSEBUTTONUP: # while bar is hold
print('1')
mouse_x, mouse_y = pygame.mouse.get_pos()
environ['SDL_VIDEO_WINDOW_POS'] = str(mouse_x - dist_x) + ',' + str(mouse_x - dist_x)
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME) # rebuild window
def main():
run = True
while run :
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60) # build frame with 60 frame per second limitation
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
print('0')
move_window()
if __name__ == '__main__':
main()
Write a function, which moves the window from dependent on a previous mouse position (start_x, start_y) and a mouse position (new_x, new_y)
def move_window(start_x, start_y, new_x, new_y):
global window_size_x, window_size_y
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
dist_x, dist_y = new_x - start_x, new_y - start_y
environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
# Windows HACK
window_size_x += 1 if window_size_x % 2 == 0 else -1
screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
In this function is a very important line:
window_size_x += 1 if window_size_x % 2 == 0 else -1
this line changes the width of the window from alternately by +1 and -1. On Windows systems there seems to be a bug, which ignores the new position parameter, if the size of the window didn't change.
This "hack" is a workaround, which slightly change the size of the window whenever the position is changed.
A different approach, with no flickering, may look as follows. Note, though, that this version is significantly slower:
def move_window(start_x, start_y, new_x, new_y):
global window_size_x, window_size_y
buffer_screen = pygame.Surface((window_size_x, window_size_y))
buffer_screen.blit(pygame.display.get_surface(), pygame.display.get_surface().get_rect())
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
dist_x, dist_y = new_x - start_x, new_y - start_y
environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
window_size_x += 1 if window_size_x % 2 == 0 else -1
screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
screen.blit(buffer_screen, buffer_screen.get_rect())
pygame.display.flip()
Change the position on MOUSEMOTION and MOUSEBUTTONUP:
def main():
run = True
pressed = False
start_pos = (0,0)
while run :
# [...]
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
pressed = True
start_pos = pygame.mouse.get_pos()
elif event.type == MOUSEMOTION:
if pressed:
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
pygame.event.clear(pygame.MOUSEBUTTONUP)
elif event.type == MOUSEBUTTONUP:
pressed = False
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
Full example program:
# coding : utf-8
import pygame
from pygame.locals import *
from os import environ
pygame.init()
clock = pygame.time.Clock()
window_size_x, window_size_y = 720, 360
infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w/2)) + ',' + str(int(infos.current_h/2))
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)
def move_window(start_x, start_y, new_x, new_y):
global window_size_x, window_size_y
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
dist_x, dist_y = new_x - start_x, new_y - start_y
environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
window_size_x += 1 if window_size_x % 2 == 0 else -1
screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
def main():
run = True
pressed = False
start_pos = (0,0)
while run :
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.type == MOUSEBUTTONDOWN:
pressed = True
start_pos = pygame.mouse.get_pos()
elif event.type == MOUSEMOTION:
if pressed:
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
pygame.event.clear(pygame.MOUSEBUTTONUP)
elif event.type == MOUSEBUTTONUP:
pressed = False
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
if __name__ == '__main__':
main()
This solution no longer works under Windows systems and with Pygame 2.0. The position of a window can, however, be changed with the WINAPI function MoveWindow:
import pygame
from ctypes import windll
pygame.init()
screen = pygame.display.set_mode((400, 400), pygame.NOFRAME)
clock = pygame.time.Clock()
def moveWin(new_x, new_y):
hwnd = pygame.display.get_wm_info()['window']
w, h = pygame.display.get_surface().get_size()
windll.user32.MoveWindow(hwnd, new_x, new_y, w, h, False)
window_pos = [100, 100]
moveWin(*window_pos)
run = True
while run :
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
elif event.type == pygame.MOUSEMOTION:
if pygame.mouse.get_pressed()[0]:
window_pos[0] += event.rel[0]
window_pos[1] += event.rel[1]
moveWin(*window_pos)
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60)
This code use only one for event loop with MOUSEBUTTONDOWN to set moving = True, MOUSEBUTTONUP to set moving = False and MOUSEMOTION which changes window's position when moving is True.
After move I use pygame.event.clear(pygame.MOUSEBUTTONUP) to remove this type of events because new window was getting this even and it was stoping window.
import pygame
from os import environ
# --- constants --- (UPPER_CASE_NAMES)
WINDOW_WIDTH = 720
WINDOW_HEIGHT = 360
# --- main ---
def main():
pygame.init()
infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = '{},{}'.format(infos.current_w//2, infos.current_h//2) # center the window
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.NOFRAME)
moving = False
clock = pygame.time.Clock()
run = True
while run:
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60) # build frame with 60 frame per second limitation
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if not moving:
print('MOUSEBUTTONDOWN')
moving = True
# remeber start distance
#window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
window_x, window_y = map(int, environ['SDL_VIDEO_WINDOW_POS'].split(','))
dist_x = event.pos[0] # mouse x
dist_y = event.pos[1] # mouse y
elif event.type == pygame.MOUSEBUTTONUP:
if moving:
print('MOUSEBUTTONUP')
moving = False
elif event.type == pygame.MOUSEMOTION:
if moving:
print('moving')
mouse_x, mouse_y = pygame.mouse.get_pos()
diff_x = dist_x - mouse_x
diff_y = dist_y - mouse_y
window_x -= diff_x
window_y -= diff_y
environ['SDL_VIDEO_WINDOW_POS'] = "{},{}".format(window_x, window_y)
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.NOFRAME) # rebuild window
pygame.event.clear(pygame.MOUSEBUTTONUP) # to remove MOUSEBUTTONUP event which stops moving window
if __name__ == '__main__':
main()
The code's not ready, but I was getting there.
I had to abandon it, but it might help someone save some time.
Press f for full-screen mode. The part that is not done,
supposed to be the window-mode resize part. You must have an in-depth
look at toogle_resize() function for that.
The resize it's taking full desktop resolution and compare it
to the space between clik (MOUSEBUTTONDOWN) and (MOUSEBUTTONUP).
Or at least that's how I wanted it to work. Good luck!
Also the code needs to be optimized, it is raw.
import pyautogui
import pygame
import sys
from pygame.locals import *
from pyinput.mouse import Controller
Mouse controller gets the position on desktop, but I had to run it once more inside the while loop to get the updated values.
mouse = Controller()
standard = current_mouse_position = mouse.position
pygame.init()
Silkscreen = False
blueGray = (73, 111, 135)
width, height = pyautogui.size()
w = width / 4
h = height / 4
ww = width - w
hh = height - h
wi = ww - 4
hi = hh - 4
Set the background and the flags
room = pygame.image.load('img.png')
full_flags = pygame.FULLSCREEN | pygame.SCALED |
pygame.NOFRAME
normal_flags = pygame.NOFRAME | pygame.RESIZABLE |
pygame.SCALED
def toggle_fullscreen(f):
if f:
return pygame.display.set_mode((ww, hh), full_flags)
# pygame.display.set_mode(size, normal_flags) # uncomment
this to see issue being fixed as a workaround
return pygame.display.set_mode((ww, hh), normal_flags)
def toggle_resize(click):
if click:
return pygame.display.set_mode((ww + movement, hh),
normal_flags) # Expands by resize_y
# Set up the drawing window
screen = pygame.display.set_mode((ww, hh), normal_flags)
def bg():
screen.blit(room, (0, 0))
def border():
pygame.draw.rect(screen, blueGray, (0, 0, ww, hh), 2) #
width = 3
clock = pygame.time.Clock()
# Run until the user asks to quit
running = True
while running:
current_mouse_position = mouse.position
# print(current_mouse_position[0])
bg()
mw, mh = pygame.mouse.get_pos() # 0 - 1439(window size)
In the next line, I checked if the mouse is on the window border. Apply only to the left, right border. You must create code for the top, bottom border. Left, right borders, 3 pixels range each side.
if mw <= 3 or (mw > wi and mw < ww):
active = True
moveOne = 0
# print("data", moveOne)
# print(type(moveOne))
moveTwo = 0
# event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
sys.exit()
# print(mw, mh)
If the user clicks, the standard variable at the beginning of the code it gets the mouse current position. You must run the active bool and check if it is True if you want to start resizing from the border of the window.
The code is very complicated and needs some optimization, but I bet you gonna get it right. Good luck!;)
if event.type == pygame.MOUSEBUTTONDOWN:
standard = current_mouse_position[0]
print(standard)
if event.type == pygame.MOUSEBUTTONUP:
moveTwo = current_mouse_position[0]
movement = standard - moveTwo
print("This is:", moveTwo)
toggle_resize(click=MOUSEBUTTONUP)
active = False
Full-screen handler from here down. Press f for full screen and back to window mode.
if event.type == pygame.KEYDOWN:
Silkscreen = not Silkscreen
if event.key == K_f:
screen = toggle_fullscreen(Silkscreen)
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
border()
# Flip the display
pygame.display.flip()
clock.tick(144) # framerate
# Done! Time to quit.
pygame.quit()'
Here is a version of #Rabbid76's answer for Pygame 2. Note that this example may break as the module _sdl2.video, used to set the window position, is experimental.
move_window.py
import pygame
from pygame._sdl2.video import Window
start_pos = pygame.Vector2(0, 0) #Initial mouse position
pressed = False #Flag that denotes when the mouse is being continuously pressed down
def move_window(window : Window, start_mouse_pos : pygame.Vector2, new_mouse_pos : pygame.Vector2) -> None:
"""Moves the window by the offset between start_mouse_pos and new_mouse_pos"""
screen = pygame.display.get_surface()
buffer_screen = pygame.Surface((window.size[0], window.size[1]))
buffer_screen.blit(screen, screen.get_rect())
window_pos_Vec2 = pygame.Vector2(window.position)
window.position = window_pos_Vec2 + new_mouse_pos - start_mouse_pos
screen.blit(buffer_screen, buffer_screen.get_rect())
pygame.display.flip()
def check_event(window : Window, event : pygame.event, move_area : pygame.Rect = pygame.Rect(-1, -1, 1, 1)) -> None:
"""Takes a window and event and updates the window position accordingly. \n
move_area can be used to set what area of the screen can be clicked in order to move the window. \n
move_area defaults to a dummy rect which is then internally changed to the full window."""
global start_pos, pressed
if move_area == pygame.Rect(-1, -1, 1, 1):
move_area = pygame.Rect((0, 0), window.size)
mouse_pos = pygame.Vector2(pygame.mouse.get_pos())
if move_area.collidepoint(mouse_pos):
if event.type == pygame.MOUSEBUTTONDOWN:
pressed = True
start_pos = mouse_pos
elif event.type == pygame.MOUSEMOTION and pressed:
move_window(window, start_pos, mouse_pos)
elif event.type == pygame.MOUSEBUTTONUP:
pressed = False
move_window(window, start_pos, mouse_pos)
else:
pressed = False
And in your main file:
import pygame
from pygame._sdl2.video import Window
screen = pygame.display.set_mode(...)
window = Window.from_display_module()
#...
while True:
for event in pygame.event.get():
#...
move_window.check_event(window, event)
I can not figure out how to generate the food for the snake to eat. I know the position of the snake at line 97 and 98, I have created a class to generate a pixel where I want to draw a peace of food at line 22 (EDIT: should probably be a function, commented #def (?) in the code). All I have to do is add 15 pixels at the x and y coordinates from the position that is randomly allocated and print it to get a block.
The problem is to check if I eat it or not. It should be something like:
if x >= x_food && x <= x_food + 15 || y >= y_food && y <= y_food + 15:
...add a point and make snake longer...
The problem is putting it all together for some reason.. Can some one give me a hint or solve how I should write this class so I can continue with other problems? Thank you!
import pygame
import random
#Global variables
#Color
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Start length of snake
snake_length = 3
#Set the width of the segments of the snake
segment_width = 15
segment_height = 15
# Margin within each segment
segment_margin = 3
#Set initial speed
x_change = segment_width + segment_margin
y_change = 0
#def (?)
class Food():
#Class to print food
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
class Segment(pygame.sprite.Sprite):
""" Class to represent the segment of the snake. """
# Methods
# Constructer function
def __init__(self, x, y):
#Call the parents constructor
super().__init__()
#Set height, width
self.image = pygame.Surface([segment_width, segment_height])
self.image.fill(WHITE)
#Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#Call this function so the Pygame library can initialize itself
pygame.init()
#Create an 800x600 size screen
screen = pygame.display.set_mode([800, 600])
#Set the title of the window
pygame.display.set_caption("Snake")
allspriteslist = pygame.sprite.Group()
#Create an initial snake
snake_segments = []
for i in range(snake_length):
x = 250 - (segment_width + segment_margin) * i
y = 30
segment = Segment(x, y)
snake_segments.append(segment)
allspriteslist.add(segment)
clock = pygame.time.Clock()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Set the speed based on the key pressed
#We want the speed to be enough that we move a full
#Segment, plus the margin
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = (segment_width + segment_margin) * -1
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = (segment_width + segment_margin)
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = (segment_height + segment_margin) * -1
if event.key == pygame.K_DOWN:
x_change = 0
y_change = (segment_width + segment_margin)
#Get rid of last segment of the snake
#.pop() command removes last item in list
old_segment = snake_segments.pop()
allspriteslist.remove(old_segment)
#Figure out where new segment will be
x = snake_segments[0].rect.x + x_change
y = snake_segments[0].rect.y + y_change
segment = Segment(x, y)
#Insert new segment to the list
snake_segments.insert(0, segment)
allspriteslist.add(segment)
#Draw
#Clear screen
screen.fill(BLACK)
allspriteslist.draw(screen)
#Flip screen
pygame.display.flip()
#Pause
clock.tick(5)
pygame.quit()
i took your code and i think i work something out, pls note that this only monitors when the snake goes over the block, then it prints: yummy, so you will have to add the detail, also note that i dont use your class to generate the food:
import pygame
import random
#Global variables
#Color
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Start length of snake
snake_length = 3
#Set the width of the segments of the snake
segment_width = 15
segment_height = 15
# Margin within each segment
segment_margin = 3
#Set initial speed
x_change = segment_width + segment_margin
y_change = 0
#def (?)
class Food():
#Class to print food
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
class Segment(pygame.sprite.Sprite):
""" Class to represent the segment of the snake. """
# Methods
# Constructer function
def __init__(self, x, y):
#Call the parents constructor
super().__init__()
#Set height, width
self.image = pygame.Surface([segment_width, segment_height])
self.image.fill(WHITE)
#Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#Call this function so the Pygame library can initialize itself
pygame.init()
#Create an 800x600 size screen
screen = pygame.display.set_mode([800, 600])
#Set the title of the window
pygame.display.set_caption("Snake")
allspriteslist = pygame.sprite.Group()
#Create an initial snake
snake_segments = []
for i in range(snake_length):
x = 250 - (segment_width + segment_margin) * i
y = 30
segment = Segment(x, y)
snake_segments.append(segment)
allspriteslist.add(segment)
clock = pygame.time.Clock()
done = False
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Set the speed based on the key pressed
#We want the speed to be enough that we move a full
#Segment, plus the margin
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = (segment_width + segment_margin) * -1
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = (segment_width + segment_margin)
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = (segment_height + segment_margin) * -1
if event.key == pygame.K_DOWN:
x_change = 0
y_change = (segment_width + segment_margin)
if y < y_food+30:
if x > x_food and x < x_food+30 or x+20 > x_food and x+20<x_food+30:
print('yummy')
#Get rid of last segment of the snake
#.pop() command removes last item in list
old_segment = snake_segments.pop()
allspriteslist.remove(old_segment)
#Figure out where new segment will be
x = snake_segments[0].rect.x + x_change
y = snake_segments[0].rect.y + y_change
segment = Segment(x, y)
#Insert new segment to the list
snake_segments.insert(0, segment)
allspriteslist.add(segment)
#Draw
#Clear screen
screen.fill(BLACK)
pygame.draw.rect(screen, WHITE, [x_food, y_food, 30, 30])
allspriteslist.draw(screen)
#Flip screen
pygame.display.flip()
#Pause
clock.tick(5)
pygame.quit()
hope this helped, thanks!
if x >= x_food && x <= x_food + 15 || y >= y_food && y <= y_food + 15:
Why do you OR these pairs of conditions? Don't all 4 tests have to be true at the same time?
if x >= x_food && x <= x_food + 15 && y >= y_food && y <= y_food + 15:
how about using:
if snake_headx == foodx and snake_heady == foody:
food_eated()
snake_grow()
just a suggestion though
didnt read all ur code, just thought u might find it usefull.
actuely ive came to a solution, so basicly what you want is a square, and when the snake comes near that square something should happen?, well ive got a racegame that makes you crash when you hit a square car, so il just copy the code here:
if y < thing_starty+thing_height:
if x > thing_startx and x < thing_startx+thing_width or x+car_width > thing_startx and x+car_width<thing_startx+thing_width:
snake_eated()
snake_grow()
this monitors the x and y of your car (or snake) and checks when the thing (or food) 's y is smaller than your car, then it checks the x's and alot of other things, and basicly it creates a big line all around your square that you cannot cross in your case you'd just need to add the rest, would this work?
Make your food a sprite with a simple filled rectangle as the image, and then use sprite collision pygame.sprite.spritecollide() to check if your snake collides with your food. Pygame will take care of the actual logic whether two rectangles overlap for you.
Also, since you are already using sprite groups, I suggest you write an update function for your snake segments which moves them instead of creating a new segment every turn. Then you can simply call allspriteslist.update() in your main game loop, which will call the update function for every snake segment.
Finally, you might want to have a look at the numerous snake examples on the pygame website.