6

I am trying to recreate snake in pygame (python 3) and what I am trying to do is every frame, check the velocity of the snake by checking keypress, but it very rarely realises that I am pressing a key, what am I doing wrong/what should I do instead (code is below), I don't see why this isn't working as everything else can run instantly, e.g the clear function, and even handle() which does a very similar thing, so it makes no sense to me as to why it doesn't work

import pygame
from pygame.locals import *
import math
import random

pygame.init()
display = pygame.display.set_mode((512, 512))
pygame.display.set_caption("Snake")
display.fill((255, 255, 255))

def handle():
    global x, y
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()

def make_apple():
    x, y = random.randint(0, 502), random.randint(0, 502)
    pygame.draw.rect(display, (255, 0, 0), (x, y, 10, 10))
    return x, y

# -- COLLISION DETECTION -- #

def r(fox, foy, cR, sox, soy):
    dx = abs(fox - sox)
    dy = abs(foy - soy)
    if dx < cR and dy < cR:
        return True
    else:
        return False

def clear(aX, aY):
    global x, y
    display.fill((255, 255, 255))
    pygame.draw.rect(display, (255, 0, 0), (aX, aY, 10, 10))
    draw_snake(x, y)

def draw_snake(x, y):
    pygame.draw.rect(display, (0, 255, 0), (x, y, 10, 10))

def set_vel():
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            print("KEY")
            if event.key == K_LEFT:
                yVel = 0
                xVel = -1
            elif event.key == K_RIGHT:
                yVel = 0
                xVel = 1
            elif event.key == K_UP:
                yVel = -1
                xVel = 0
            elif event.key == K_DOWN:
                yVel = 1
                xVel = 0
            return xVel, yVel
    return 0, 0
def update_pos(x, y, xV, yV):
    x += xV
    y += yV
    return x, y

aX, aY = make_apple()
x, y = 256, 256
length = 1
eaten = False

while True:
    velX, velY = set_vel()
    clear(aX, aY)
    handle()
    x, y = update_pos(x, y, velX, velY)
    if eaten:
        aX, aY = make_apple()
        eaten = False
    pygame.display.update()
    if r(x, y, 3, aX, aY):
        display.fill((255, 255, 255))
        eaten = True
Qiu YU
  • 517
  • 4
  • 20
Archie
  • 96
  • 7

2 Answers2

4

Your problem is that when you call pygame.event.get(), that function not only gets the events, but also removes them from the queue. This means calling it twice per frame (as you do in set_vel and handle) can give weird results.

When I write pygame, I have one for event in pygame.event.get() loop in my while True. Try doing this and move the quit handling and velocity changing into the True loop instead of their own functions.

incarnadine
  • 658
  • 7
  • 19
3

As mentioned in the other answer pygame.event.get() get all the messages and remove them from the queue. So either the 1st or the 2nd loop gets an event, but never both loops will get all events. That causes that some events seems to be missed.

Ge the list of events once in the main application loop and pass the list to the functions:

def handle(events):
    global x, y
    for event in events:
        if event.type == QUIT:
            pygame.quit()
def set_vel(events):
    for event in events:
        # [...]
while True:
    events = pygame.event.get()
  
    velX, velY = set_vel(events )
    clear(aX, aY)
    handle(events)

    # [...] 
Rabbid76
  • 202,892
  • 27
  • 131
  • 174