Python Forum
PyGame Nibbles tutorial for beginners.
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
PyGame Nibbles tutorial for beginners.
#1
I would have summit this in tutorial request and submission. There is no post thread there.

First thing. Build a boiler plate. Opens a pygame screen.
# Step 1. Let start out with the basic boiler plate.

import os
import pygame
# global Container
class Game:
    pass

def main():
    # Center screen
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    pygame.display.set_caption("Nibbles Tutorial")
    Game.screen = pygame.display.set_mode((720,420))
    Game.running = True

    clock = pygame.time.Clock()

    # Mainloop
    while Game.running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Game.running = False

        Game.screen.fill((0,10,0))
        pygame.display.flip()
        clock.tick(30)
    pygame.quit()

if __name__ == '__main__':
    main()
Next draw a snake and basic screen rect.
It a good thing to store values in variables for later use.
# Step 2. Draw the snake

import os
import pygame
# global Container
class Game:
    pass

def main():
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    pygame.display.set_caption("Nibbles Tutorial")
    # Let define block size.
    Game.block_size = 12
    # Set blocks width
    Game.width = 72
    width = Game.block_size * Game.width
    # Set blocks height
    Game.height = 42
    height = Game.block_size * Game.height
    # Rect to keep are snake within
    Game.rect = pygame.Rect(0,0,width,height)
    Game.screen = pygame.display.set_mode(Game.rect.size)
    Game.running = True

    Game.snake_color = pygame.Color('lawngreen')
    size = Game.block_size
    x = Game.width / 2 * size
    y = Game.height / 2 * size
    # Build the snake body
    Game.snake = [(x, y), (x, y + size), (x, y + size * 2)]

    clock = pygame.time.Clock()

    # Mainloop
    while Game.running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Game.running = False

        Game.screen.fill((0,10,0))

        # Draw the snake
        for body in Game.snake:
            rect = pygame.Rect(body[0], body[1], Game.block_size, Game.block_size)
            Game.screen.fill(Game.snake_color, rect)

        pygame.display.flip()
        clock.tick(30)
    pygame.quit()

if __name__ == '__main__':
    main()
Now get the snake moving and colliding with border.
# Step 3. Make the snake move

import os
import pygame
# Global Container
class Game:
    pass

def add_tuples(lhs, rhs):
    return lhs[0] + rhs[0], lhs[1] + rhs[1]

def keys_down():
    # let get the keypressed states
    keypress = pygame.key.get_pressed()
    # press left arrow key and it not moving right
    if keypress[pygame.K_LEFT] and Game.snake_direction[0] != Game.block_size:
        # set snake direction to left
        Game.snake_direction = -Game.block_size, 0 # (-10, 0) (right or left, up or down)
    # press right arrow key and it not moving left
elif keypress[pygame.K_RIGHT] and Game.snake_direction[0] != -Game.block_size:
        # set snake direction to right
        Game.snake_direction = Game.block_size, 0
    # press up arrow key and it not moving down
elif keypress[pygame.K_UP] and Game.snake_direction[1] != Game.block_size:
        # set snake direction to up
        Game.snake_direction = 0, -Game.block_size
    # press down arrow key and it not moving up
elif keypress[pygame.K_DOWN] and Game.snake_direction[1] != -Game.block_size:
        # set snake direction to down
        Game.snake_direction = 0, Game.block_size

def snake_move():
    # Move the snake
    head = add_tuples(Game.snake[0], Game.snake_direction)
    # Add head and drop tail. Simulate forward motion
    Game.snake = [head] + Game.snake[:-1]
    # snake collide with outside wall
    if not Game.rect.collidepoint(head):
        new_game()

# Move snake here for quick restart
def new_game():
    size = Game.block_size
    # Give snake a direction
    Game.snake_direction = 0, -size # up
    x = Game.width / 2 * size
    y = Game.height / 2 * size
    Game.snake = [ (x, y), (x, y + size), (x, y + size * 2)

def main():
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    pygame.display.set_caption("Nibbles Tutorial")
    Game.block_size = 12
    Game.width = 72
    width = Game.block_size * Game.width
    Game.height = 42
    height = Game.block_size * Game.height
    Game.rect = pygame.Rect(0,0,width,height)
    Game.screen = pygame.display.set_mode(Game.rect.size)
    Game.running = True

    Game.snake_color = pygame.Color('lawngreen')
    new_game()

    clock = pygame.time.Clock()

    # Mainloop
    while Game.running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Game.running = False

        # See if key is pressed
        keys_down()
        # Move every loop
        snake_move()
        Game.screen.fill((0,10,0))
        # Draw the snake
        for body in Game.snake:
            rect = pygame.Rect(body[0], body[1], Game.block_size, Game.block_size)
            Game.screen.fill(Game.snake_color, rect)

        pygame.display.flip()
        # reduce framerate for movement
        clock.tick(10)
    pygame.quit()

if __name__ == '__main__':
    main()
Okay now let not use framerate to control speed.
Let give the snake some food to gather.
Let give the snake random start direction.
Have to have snake_head to avoid fast keys down changes.
# Step 4. Food, better snake movement timing, and random start direction

import os
import pygame
from random import choice, randrange
# Global Container
class Game:
    pass

def add_tuples(lhs, rhs):
    return lhs[0] + rhs[0], lhs[1] + rhs[1]

def sub_tuples(lhs, rhs):
    return lhs[0] - rhs[0], lhs[1] - rhs[1]

def keys_down():
    # let get the keypressed states
    keypress = pygame.key.get_pressed()
    if keypress[pygame.K_LEFT] and Game.snake_head[0] != Game.block_size:
        Game.snake_direction = -Game.block_size, 0 # (-10, 0) (right or left, up or down)
    elif keypress[pygame.K_RIGHT] and Game.snake_head[0] != -Game.block_size:
        Game.snake_direction = Game.block_size, 0
    elif keypress[pygame.K_UP] and Game.snake_head[1] != Game.block_size:
        Game.snake_direction = 0, -Game.block_size
    elif keypress[pygame.K_DOWN] and Game.snake_head[1] != -Game.block_size:
        Game.snake_direction = 0, Game.block_size

def snake_move():
    # Assign snake_head to snake_direction here
    Game.snake_head = Game.snake_direction
    # Move the snake
    head = add_tuples(Game.snake[0], Game.snake_direction)

    if not Game.rect.collidepoint(head):
        new_game()
    # Snake body collision
    elif head in Game.snake:
        new_game()
    # Food collision
    elif head == Game.food:
        Game.snake = [head] + Game.snake
        # Make snake move faster
        Game.movement_timer['length'] -= 2
        # make more food
        create_food()
    else:
        Game.snake = [head] + Game.snake[:-1]

def create_food():
    Game.food = None
    # Let make sure food doesn't collide with snake
    while Game.food is None:
        x = randrange(1, Game.width, 1) * Game.block_size
        y = randrange(1, Game.height, 1) * Game.block_size
        Game.food = (x, y)
        if Game.food in Game.snake:
            Game.food = None

    size = Game.block_size - 2
    Game.food_rect = (x + 1, y + 1, size, size)

def new_game():
    size = Game.block_size
    # random start direction
    Game.snake_direction = choice(((0, -size), (0, size), (-size, 0), (size, 0)))
    d = Game.snake_direction
    x = Game.width / 2 * size
    y = Game.height / 2 * size
    # Give snake a head to avoid opposite collision
    Game.snake_head = Game.snake_direction
    # Build snake body according to direction.
    Game.snake = [(x, y), sub_tuples((x, y), d), sub_tuples(sub_tuples((x,y), d), d)]
    # Make a Tick Timer, Ticks are in milliseconds.
    Game.movement_timer = {'length': 120, 'next tick': pygame.time.get_ticks()}
    # New food spot on every new game
    create_food()

def timer_elasped(timer, ticks):
    if ticks > timer['next tick'] + timer['length']:
        timer['next tick'] += timer['length']
        return True
    return False

def main():
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    pygame.display.set_caption("Nibbles Tutorial")
    Game.block_size = 12
    Game.width = 72
    width = Game.block_size * Game.width
    Game.height = 42
    height = Game.block_size * Game.height
    Game.rect = pygame.Rect(0,0,width,height)
    Game.screen = pygame.display.set_mode(Game.rect.size)
    Game.running = True
    Game.snake_color = pygame.Color('lawngreen')
    Game.food_color = pygame.Color('yellow')
    new_game()
    clock = pygame.time.Clock()
    # Mainloop
    while Game.running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Game.running = False

        keys_down()
        # Get pygame ticks
        ticks = pygame.time.get_ticks()
        # Only move snake if timer elasped
        if timer_elasped(Game.movement_timer, ticks):
            snake_move()

        Game.screen.fill((0,10,0))
        # Draw food
        Game.screen.fill(Game.food_color, Game.food_rect)

        # Draw the snake
        for body in Game.snake:
            rect = pygame.Rect(body[0], body[1], Game.block_size, Game.block_size)
            Game.screen.fill(Game.snake_color, rect)

        pygame.display.flip()
        clock.tick(60)
    pygame.quit()

if __name__ == '__main__':
    main()
Final let create a stage. Walls for the snake to avoid.
Create a snake surface over color.
# Step 5. Walls and better looking snake

import os
import pygame
from random import choice, randrange
# Global Container
class Game:
    pass

def add_tuples(lhs, rhs):
    return lhs[0] + rhs[0], lhs[1] + rhs[1]

def sub_tuples(lhs, rhs):
    return lhs[0] - rhs[0], lhs[1] - rhs[1]

def keys_down():
    # let get the keypressed states
    keypress = pygame.key.get_pressed()
    if keypress[pygame.K_LEFT] and Game.snake_head[0] != Game.block_size:
        Game.snake_direction = -Game.block_size, 0 # (-10, 0) (right or left, up or down)
    elif keypress[pygame.K_RIGHT] and Game.snake_head[0] != -Game.block_size:
        Game.snake_direction = Game.block_size, 0
    elif keypress[pygame.K_UP] and Game.snake_head[1] != Game.block_size:
        Game.snake_direction = 0, -Game.block_size
    elif keypress[pygame.K_DOWN] and Game.snake_head[1] != -Game.block_size:
        Game.snake_direction = 0, Game.block_size

def snake_move():
    # Assign snake_head to snake_direction here
    Game.snake_head = Game.snake_direction
    # Move the snake
    head = add_tuples(Game.snake[0], Game.snake_direction)

    if not Game.rect.collidepoint(head):
        new_game()
    # Snake body collision
    elif head in Game.snake or head in Game.walls:
        new_game()
    # Food collision
    elif head == Game.food:
        Game.snake = [head] + Game.snake
        # Make snake move faster
        Game.movement_timer['length'] -= 2
        # make more food
        create_food()
    else:
        Game.snake = [head] + Game.snake[:-1]

def create_food():
    Game.food = None
    # Let make sure food doesn't collide with snake
    while Game.food is None:
        x = randrange(1, Game.width, 1) * Game.block_size
        y = randrange(1, Game.height, 1) * Game.block_size
        Game.food = (x, y)
        # Don't have food spawn in walls
        if Game.food in Game.snake or Game.food in Game.walls:
            Game.food = None


    size = Game.block_size - 2
    Game.food_rect = (x + 1, y + 1, size, size)

def new_game():
    # First Stage
    create_stage1()
    size = Game.block_size
    # random start direction
    Game.snake_direction = choice(((0, -size), (0, size), (-size, 0), (size, 0)))
    d = Game.snake_direction
    x = Game.width / 2 * size
    y = Game.height / 2 * size
    # Give snake a head to avoid opposite collision
    Game.snake_head = Game.snake_direction
    # Build snake body according to direction.
    Game.snake = [(x, y), sub_tuples((x, y), d), sub_tuples(sub_tuples((x,y), d), d)]
    # Make a Tick Timer, Ticks are in milliseconds.
    Game.movement_timer = {'length': 120, 'next tick': pygame.time.get_ticks()}
    # New food spot on every new game
    create_food()

def timer_elasped(timer, ticks):
    if ticks > timer['next tick'] + timer['length']:
        timer['next tick'] += timer['length']
        return True
    return False

def create_snake():
    surface = pygame.Surface((Game.block_size, Game.block_size))
    surface.fill(pygame.Color('lawngreen'))
    size = Game.block_size
    mid = int(Game.block_size / 2)
    color = pygame.Color('forestgreen')
    for i in [1,2,3]:
        j = i * 2
        pygame.draw.line(surface, color, (mid - i, j), (mid - i, size - j))
        pygame.draw.line(surface, color, (mid + i, j), (mid + i, size - j))
        pygame.draw.line(surface, color, (j, mid - i), (size - j, mid - i))
        pygame.draw.line(surface, color, (j, mid + i), (size - j, mid + i))
    return surface

def create_stage1():
    # Classic first stage
    Game.walls = []
    for y in range(7, Game.height - 7):
        Game.walls.append((12 * Game.block_size, y * Game.block_size))
        Game.walls.append(((Game.width - 12) * Game.block_size, y * Game.block_size))

def main():
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    pygame.display.set_caption("Nibbles Tutorial")
    Game.block_size = 12
    Game.width = 72
    width = Game.block_size * Game.width
    Game.height = 42
    height = Game.block_size * Game.height
    Game.rect = pygame.Rect(0,0,width,height)
    Game.screen = pygame.display.set_mode(Game.rect.size)
    Game.running = True
    Game.snake_body = create_snake()
    Game.food_color = pygame.Color('yellow')
    new_game()
    clock = pygame.time.Clock()
    # Mainloop
    while Game.running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Game.running = False

        keys_down()
        # Get pygame ticks
        ticks = pygame.time.get_ticks()
        # Only move snake if timer elasped
        if timer_elasped(Game.movement_timer, ticks):
            snake_move()

        Game.screen.fill((0,10,0))
        # Draw food
        Game.screen.fill(Game.food_color, Game.food_rect)
        # Draw walls
        for wall in Game.walls:
            rect = pygame.Rect(wall[0], wall[1], Game.block_size, Game.block_size)
            Game.screen.fill(pygame.Color('gray30'), rect)
        # Draw the snake
        for body in Game.snake:
            rect = pygame.Rect(body[0], body[1], Game.block_size, Game.block_size)
            #Game.screen.fill(Game.snake_color, rect)
            Game.screen.blit(Game.snake_body, rect)

        pygame.display.flip()
        clock.tick(60)
    pygame.quit()

if __name__ == '__main__':
    main()
Challenges.
1. Rewrite this game using classes. Basic function are still aloud.
2. Make the snake grow by 2 or 3 per food.
3. Make another stage. Have it change to next stage after snake gets so big.
99 percent of computer problems exists between chair and keyboard.
Reply
#2
There is a post button there, but this section was copied over from the old forum.
You can post under game tutorials.
Reply
#3
whoops sorry.

Yes, the post option was restricted in submissions. Fixed now.

And i also moved this there.
Recommended Tutorials:
Reply
#4
If Game is just a container, and you don't use it like a class, why not make it a dictionary? Then you don't need a comment explaining that it's a bag for global state to sit in.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Basic] Python Tutorial for Beginners MK_CodingSpace 0 2,169 Dec-03-2020, 09:43 PM
Last Post: MK_CodingSpace
  [Basic] Python Tutorial for Beginners in One Lesson (98 minutes) artur_m 0 3,992 Jan-23-2020, 09:15 AM
Last Post: artur_m
  [Basic] Interactive tutorial for beginners (codetheblocks) abhin 1 3,252 Feb-21-2018, 05:09 AM
Last Post: Larz60+
  A good tutorial for beginners? (Youtube) Peter_EU 11 8,074 Dec-06-2017, 09:43 PM
Last Post: apollo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020