Skip to content

Aleskyy/pyvBreaker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tutorial Breaker

This tutorial is here to help you create a block breaker type game from scratch.

This tutorial can also be used as a nice base to understand and experiment with the ECS pattern.

Note that the game in it's final state is already available in the pyved-engine in the pyv-cli.

If you need further help for using PYV, you can always refer to the pyved-engine full docs homepage.

Step 0. Getting Started

Either Download the init, or just the template

If you want to build upon the foundation of the breaker template, you can download it by doing the following

pyv-cli init myGame
pyv-cli play myGame

And choose the Breakout template.

Note that this tutorial won't focus on building upon the foundation, instead the focus is on how to build on nothing and manage to create such a game.

So for this, we will choose the empty template instead.

Your folder should look something like this.

.
└── YourBundleName/
    ├── cartridge/
    │   ├── __pycache__/
    │   ├── gamedef.py
    │   ├── __init__.py
    │   ├── metadat.json
    │   └── pimodules.py
    ├── __pycache__/
    ├── __init__.py
    ├── vm.py
    └── vmslateL.py

Step 1 : Create the necessary files for the ECS

As of now, all of the files used in an ECS configuration aren't auto generated, this might change in the future, but since you're here to learn everything on how to create a game using Pyved and ECS, let's get into it.

Go inside cartridge and create the following file, they'll be useful later :

  • shared.py
  • systems.py
  • world.py

These files will store nearly all of your ECS code.

Step 2 : Creating the foundations

In this part, you will learn how to create your first entities and draw them on your screen.

Creation of Archetypes and Entities

First, let's create all of our constants in our shared.py :

import random


screen = None
blocks_pop = None

# (Size of break-out blocks)
BLOCK_W = 54
BLOCK_H = 30
BLOCK_SPACING = 2
WALL_X, WALL_Y = 4, 80
PLAYER_SPEED = 10

# ball-related
BALL_INIT_POS = 480, 277
BALL_SIZE = 22
RANDOM_DIR = random.uniform(-5.0, 5.0)

We will use all of those constants laters, but they are important.

Secondly we will create all of our entities, let's head inside gamedef.py

from . import pimodules
from . import shared
from . import systems
from .world import world

pyv = pimodules.pyved_engine
@pyv.declare_begin
def init_game(vmst=None):
    pyv.init(wcaption='Néant')

Let's create our archetypes, this is how we will create the different entities :

    pyv.define_archetype('player', (
        'speed', 'controls', 'body'
    ))
    pyv.define_archetype('block', ('body', ))
    pyv.define_archetype('ball', ('body', 'speed'))

We can now go into world.py, to create our entities :

from . import shared
from . import pimodules
pyv = pimodules.pyved_engine

pygame = pyv.pygame


class world:

    def create_player():
        player = pyv.new_from_archetype('player')
        pyv.init_entity(player, {
            'speed': 0.0, 
            'controls':{'left': False, 'right': False},
            'body': pygame.rect.Rect(900,600, shared.BLOCK_W, shared.BLOCK_H)
        })

We are creating a create_player function, this will create the entities with our default settings, we will do the same for every entity present in our game.

We give it, a speed, some controls, here we only give it left and right, as we don't need other directions.

And finally a body, so that our player have a hitbox.

    def create_ball():
        ball = pyv.new_from_archetype('ball')
        pyv.init_entity(ball, {
            'speed': 0.0, 
            'body': pygame.rect.Rect(shared.BALL_INIT_POS[0],shared.BALL_INIT_POS[1], shared.BALL_SIZE, shared.BALL_SIZE)
        })

We will create our ball too, we will keep the same logic, and create, it needs a speed to move, and a hitbox to detect collisions.

def create_blocks():
        bcy = 0 
        LIMIT = 960/(shared.BLOCK_W + shared.BLOCK_SPACING)
        for column in range(5):
            bcy = bcy+shared.BLOCK_H + shared.BLOCK_SPACING
            bcx = -shared.BLOCK_W
            for row in range(round(LIMIT)):
                bcx = bcx +shared.BLOCK_W + shared.BLOCK_SPACING
                rrect = pygame.rect.Rect(0 + bcx, 0 + bcy, shared.BLOCK_W, shared.BLOCK_H)

                pyv.init_entity(pyv.new_from_archetype('block'),{
                    'body': rrect
                })

Here the code is a bit different, since there's more than one block, we will loop over it to generate as many entity as needed.

Here we fill the screen from left to right, and on 5 row with blocks.

We can now head back into gamedef.py and finalise the creation of our entities.

@pyv.declare_begin
def init_game(vmst=None):
    pyv.init()
    pyv.define_archetype('player', (
        'speed', 'controls', 'body'
    ))
    pyv.define_archetype('block', ('body', ))
    pyv.define_archetype('ball', ('body', 'speed'))
    world.create_player()
    world.create_ball()
    world.create_blocks()
    pyv.bulk_add_systems(systems)

Here we added the call to the different functions we made in the world.py, so that now, when we launch our game, we create actual entities instead of just their archetypes.

As for the line pyv.bulk_add_systems(systems) we will get back to it soon.

Creating systems

Let's head inside systems.py :

from . import shared
from . import pimodules
pyv = pimodules.pyved_engine
pyv.bootstrap_e()

__all__ = [

    'gamectrl_sys'
]

def gamectrl_sys(entities, components):
    pg = pyv.pygame
    for ev in pg.event.get():
        if ev.type == pg.KEYDOWN and ev.key == pg.K_ESCAPE:
            pyv.vars.gameover = True 

This is a base setup for a systems.py file, let's make it a bit more full with all of our base systems.

from . import shared
from . import pimodules

pyv = pimodules.pyved_engine
pyv.bootstrap_e()

__all__ = [
    'controls_sys',
    'physics_sys',
    'rendering_sys',
    'gamectrl_sys'
]

def controls_sys(entities, components):
    #Empty for now
            
def physics_sys(entities, components):
    #Empty for now

def rendering_sys(entities, components):
    #Empty for now
        
def gamectrl_sys(entities, components):
    pg = pyv.pygame
    for ev in pg.event.get():
        if ev.type == pg.KEYDOWN and ev.key == pg.K_ESCAPE:
            pyv.vars.gameover = True 

Now, we will create our rendering systems, to at least have some graphics and wrap this step up.

def rendering_sys(entities, components):
    """
    displays everything that can be rendered
    """
    scr = shared.screen

    scr.fill((0, 0, 0))
    pl_ent = pyv.find_by_archetype('player')[0]
    li_blocks = pyv.find_by_archetype('block')
    ball = pyv.find_by_archetype('ball')[0]

    pyv.draw_rect(scr, 'white', pl_ent['body'])
    pyv.draw_rect(scr, 'blue', ball['body'])
    for b in li_blocks:
        pyv.draw_rect(scr, 'purple', b['body'])

What we do here, is first fill the screen in black, then find the different lists of entities for each archetypes. We then simply give their hitbox a graphical representation with different colors. Since the player and ball are alone, we just go for the first one of the list, and we iterate over the different blocks we created before.

Let's head one last time into gamedef.py, and add the few details missing in order to display everything fine.

@pyv.declare_begin
def init_game(vmst=None):
    pyv.init()
    screen = pyv.get_surface() 
    shared.screen = screen
    pyv.define_archetype('player', (
        'speed', 'controls', 'body'
    ))
    pyv.define_archetype('block', ('body', ))
    pyv.define_archetype('ball', ('body', 'speed'))
    world.create_player()
    world.create_ball()
    world.create_blocks()
    pyv.bulk_add_systems(systems)

Here we added the screen creation to our initialisation sequence, this is also where the bulk system is used, since we created a rendering system, we need to load it in our game.

Now, in order to have something displays and refresh, we will also modify our update like so:

@pyv.declare_update
def upd(time_info=None):
    pyv.systems_proc()
    pyv.flip()

Here we use our systems, then use flip in order to display everything.

And now, your game should look something like this :

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages