• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
EricsonWillians

The Pygrid Engine (Pygame)

3 posts in this topic

EDIT: The engine was updated to a new version (1.1).


I would like to introduce you to the engine that I'm developing in Python/Pygame. It is open-source and cross-platform, and it is pretty simple to understand. It is in its very beginning, but it's possible to grasp the main principle already. I'll update it constantly. (Soon I'll introduce a background-redrawing method, and methods to deal with images and sprite animations on a grid-based approach (And also collision-detection)). If you have some technical knowledge, you can understand it all and implement them already (The engine is not complicated).

Download link on sourceforge: https://sourceforge.net/projects/pygrid/
My introduction video on Youtube: http://www.youtube.com/watch?v=T0F_ODYdRDs

The zip file has two files: pygrid.py and main.py.
The main.py is just a simple pygame program introducing the Pygrid classes.

pygrid.py

"""

====================================================================

PYGRID ENGINE 1.0
Copyright (C) <2014>  <Ericson Willians.>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

====================================================================
Engine written by Ericson Willians, a brazilian composer and programmer.

CONTACT: ericsonwrp@gmail.com
AS A COMPOSER: https://soundcloud.com/r-p-ericson-willians
YOUTUBE CHANNEL: http://www.youtube.com/user/poisonewein

====================================================================
"""

__author__ = 'EricsonWillians'

from pygame import *

class Grid():

    # A list for the grid's width and height, and a list for the width and height of each individual rect in the grid.
    # Lists are mutable, and, therefore, it's possible to change the grid in real-time easily.
    def __init__(self, wh, rwh):
        self.gridRectWidth = rwh[0]
        self.gridRectHeight = rwh[1]
        self.gridWidth = wh[0]
        self.gridHeight = wh[1]
        self.gridWidthInPixels = rwh[0]*wh[0]
        self.gridHeightInPixels = rwh[1]*wh[1]
        # X and Y positions as lists (They can be altered).
        self.keys = [[x for x in range(self.gridWidth)], [x for x in range(self.gridHeight)]]
        # X and Y keys. Each key represents a real value in pixels. All positioning on the grid is made through keys.
        self.x = dict(zip([x for x in self.keys[0]], [x for x in range(0, self.gridWidthInPixels, self.gridRectWidth)]))
        self.y = dict(zip([x for x in self.keys[1]], [x for x in range(0, self.gridHeightInPixels, self.gridRectHeight)]))
        self.colors = {"BLACK": (0,0,0), "WHITE": (255,255,255), "RED": (255,0,0), "GREEN": (0,255,0), "BLUE": (0,0,255)}

    # These two methods return the true position in pixels of a key.
    def getX(self, key):
        return self.x.get(key)

    def getY(self, key):
        return self.y.get(key)

# A grect, or GRID RECTANGLE, is an object.
# All grects have grid-fixed sizes (That's the main philosophy behind this engine):
# Perfect controls and collision-detection giving the grid-fixed movement/interaction of objects.
class Grect():

    def __init__(self, x, y, grid, surface, color):
        self.x = grid.getX(x) # Its real x position in pixels.
        self.y = grid.getY(y) # # Its real y position in pixels.
        self.color = color

    def draw(self, surface, color, rect):
        try: # If the grect position is offset the gridsize, it raises a TypeError, and here we avoid it.
            draw.rect(surface, color, rect)
        except: # So that when you're offset, it just does not draw it.
            pass

    # The move method moves the grect in the grid in a specific direction by steps.
    # It takes a grid argument, because the game could have more than one grid.
    def move(self, direction, step, grid):
        if direction == "UP":
            self.y -= grid.getX(step)
        elif direction == "DOWN":
            self.y += grid.getX(step)
        elif direction == "LEFT":
            self.x -= grid.getX(step)
        elif direction == "RIGHT":
            self.x += grid.getX(step)

# To be continued...

main.py
 

"""

====================================================================

PYGRID ENGINE 1.0
Copyright (C) <2014>  <Ericson Willians.>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

====================================================================
Engine written by Ericson Willians, a brazilian composer and programmer.

CONTACT: ericsonwrp@gmail.com
AS A COMPOSER: https://soundcloud.com/r-p-ericson-willians
YOUTUBE CHANNEL: http://www.youtube.com/user/poisonewein

====================================================================
"""

__author__ = 'EricsonWillians'

from pygame import *
from random import *
from pygrid import *

init()

# The Grid() constructor takes two arguments: A list of two objects for the grid size in width and height,
# and another one for the width and height of each individual rectangle in the grid.
grid = Grid([20, 20], [30, 30])
speed = 16
clock = time.Clock()
done = False
# The screen has the size of the grid. Essential for the grid-fixed principle of perfect rect-interaction.
screen = display.set_mode((grid.gridWidthInPixels, grid.gridHeightInPixels))
display.set_caption(str(grid.gridWidth) + " by " + str(grid.gridHeight) + " Pygrid")

# Grects.
# ============================================
def createGrect(x, y, grid, color):
     g = Grect(x, y, grid, screen, color)
     return g

player = createGrect(0, 0, grid, (255,0,0))
# ============================================

def update():

    player.draw(screen, (255,0,0), (player.x, player.y, grid.gridRectWidth, grid.gridRectHeight))

    display.update() # Pygame default display-thing.
    display.flip() # It updates the whole thing at each frame of the loop, or something like that.
    clock.tick(speed) # The pygame speed, or something like that.

while not done:
    keys = key.get_pressed()
    if keys[K_UP]:
        player.move("UP", 1, grid)
    if keys[K_DOWN]:
        player.move("DOWN", 1, grid)
    if keys[K_LEFT]:
        player.move("LEFT", 1, grid)
    if keys[K_RIGHT]:
        player.move("RIGHT", 1, grid)

    for e in event.get():

        if e.type == QUIT or keys[K_ESCAPE]:
            done = True

    update()

quit()
Edited by EricsonWillians
0

Share this post


Link to post
Share on other sites

Looks like a good start smile.png

 

Just some feedback on your code style:

  • For modules, classes, methods and functions you might want to use python docstrings instead of using '#'. A docstring is much more useful as any decent IDE can show it to you without you needing to visit the file to read the comment.
  • It's generally not good python style to use import *. Better to import exactly what you need so there is no ambiguity. ( e.g, the "init" function in main.py, where is that defined? Is it pygame, random or pygrid? ( I know it's pygame in this script, but as your codebase grows bigger, you might want to provide your own init()  ) )
  • I know it's only a demo script, but all the "global" code in main.py would look a lot nicer grouped into a function ( or an if __name__ == "__main__" ), just to make it more readable. As it is, you kinda need to jump around to follow the code.

 

You don't have to take any of this on board of course, the code will work regardless. At work we develop primarily in python and it's great working with clean documented code when using 3rd party packages. These would be things I would be looking out for if I was searching for a python engine to use making a game.

 

All the best with this endeavour smile.png

1

Share this post


Link to post
Share on other sites

Docstrings? I had completely forgotten about them haha. Fantastic observation about the docstrings and IDEs. The "main.py" was not exactly meant to be an "official" file, but I'll follow your advice and group them in a function (And make it more official). The "main.py" is important to show in practice how to use the module. And I agree corcerning the "ambiguity" problem, I ignored that detail as well.

I thank you very much for your help :). I'll post the updates here in the forum.

1

Share this post


Link to post
Share on other sites

I have completely updated the engine, and released the 1.1 version. I have also recorded a video (51 min) explaining and showing what it is possible to do with the pygrid engine 1.1. It is much more powerful now.

Video: http://www.youtube.com/watch?v=iaTFwnZz8L8

 

ScoreX, I've followed your tips (The whole code is properly documented now), I've used just "import", and grouped the "global" code in an "if __name__ == "__main__"". Here's the whole updated code:

pygrid.py 1.1

"""
====================================================================

PYGRID ENGINE 1.1
Copyright (C) <2014>  <Ericson Willians.>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

====================================================================
Engine written by Ericson Willians, a brazilian composer and programmer.

CONTACT: ericsonwrp@gmail.com
AS A COMPOSER: https://soundcloud.com/r-p-ericson-willians
YOUTUBE CHANNEL: http://www.youtube.com/user/poisonewein

====================================================================
"""

__author__ = 'EricsonWillians'

import pygame

class Grid():

    """
    A Pygrid Grid is a tessellation of n-dimensional Euclidean space by congruent bricks.
    """

    def __init__(self, wh, rwh):

        """
        The Grid constructor defines its size in width and height by elements.
        Every element is a grid rectangle, with a x and y position and fixed size.
        Both (wh) and (rwh) arguments are list objects of two indexes.

        self refers to each individual grid instance.
        The wh argument expects a list with two indexes (One for the width, one for the height).
        the rwh argument expects a list with two indexes (One for the fixed width of each rectangle, one for the fixed height of each rectangle)

        The keys list has two indexes.
        keys[0] is the width of the grid in elements.
        keys[1] is the height of the grid in elements.

        The x dictionary has keys[0] number of keys.
        Each key[0] key in x dictionary has an equivalent int value in pixels.
        The same applies to the y dictionary.

        Colors dictionary has basic colors as values of capitalized color-name string keys..
        """

        self.gridRectWidth = rwh[0]
        self.gridRectHeight = rwh[1]
        self.gridWidth = wh[0]
        self.gridHeight = wh[1]
        self.gridWidthInPixels = rwh[0]*wh[0]
        self.gridHeightInPixels = rwh[1]*wh[1]
        self.keys = [[x for x in range(self.gridWidth)], [x for x in range(self.gridHeight)]]
        self.x = dict(zip([x for x in self.keys[0]], [x for x in range(0, self.gridWidthInPixels, self.gridRectWidth)]))
        self.y = dict(zip([x for x in self.keys[1]], [x for x in range(0, self.gridHeightInPixels, self.gridRectHeight)]))
        self.colors = {"BLACK": (0,0,0), "WHITE": (255,255,255), "RED": (255,0,0), "GREEN": (0,255,0), "BLUE": (0,0,255)}

    def getX(self, key):

        """
        The getX() method expects a x-key as an argument. It returns its equivalent value in pixels.
        """

        return self.x.get(key)

    def getY(self, key):

        """
        The getY() method expects a y-key as an argument. It returns its equivalent value in pixels.
        """

        return self.y.get(key)

class Grect():

    """
    A Pygrid Grect is a grid rectangle.
    """

    def __init__(self, grid, x, y, color, isBackground):

        """
        The Grect constructor defines its position on the specified grid and its color.

        The grect x position in pixels is the specified x-key.
        The grect y position in pixels is the specified y-key.
        The grect width and height are grid-fixed (But they can be altered).
        The grect color is the specified color.
        The boolean defines if it shall be used as a background for the whole grid.
        """

        self.grid = grid
        self.x = grid.getX(x)
        self.y = grid.getY(y)
        self.w = grid.gridRectWidth
        self.h = grid.gridRectHeight
        self.color = color
        self.isBackground = isBackground

    def changeColor(self, color):

        """
        The changeColor() method expects a new color as an argument (A RGB-tuple with 3 indexes).
        """

        self.color = color

    def draw(self, surface):

        """
        The draw() method draws the grect in its current x and y positions.
        If the grect position is offset within the grid limits, it raises a TypeError.
        If it is a background, then it draws the grect of the size of the whole grid.
        """

        if self.isBackground == False:
            try:
                pygame.draw.rect(surface, self.color, (self.x, self.y, self.w, self.h))
            except:
                pass
        elif self.isBackground == True:
            try:
                pygame.draw.rect(surface, self.color, (0, 0, self.grid.gridWidthInPixels, self.grid.gridHeightInPixels))
            except:
                pass

    def getGrid(self):

        """
        Returns the associated grid instance.
        """

        return self.grid

    def getColor(self):

        """
        Returns the grect RGB color (Tuple).
        """

        return self.color

    def isBackground(self):

        """
        Returns the isBackground boolean.
        """

        return self.isBackground

class GrectArray():

    """
    A Pygrid GrectArray is a sequence of grid rectangles.
    """

    def __init__(self, grid, isBackground):

        """
        The constructor defines to the GrectArray instance its own list and a boolean indicating if it is used as a background.
        """

        self.grid = grid
        self.array = []
        self.isBackground = isBackground

    def add(self, grect):

        """
        The add() method adds to the GrectArray instance a specified grect.
        """

        self.array.append(grect)

    def remove(self, grect):

        """
        The remove() method removes from the GrectArray the specified grect instance (If it exists).
        """

        if len(self.array) > 0:
            for i in self.array:
                if i == grect:
                    self.array.remove(grect)

    def addBackground(self, color):

        """
        The addBackground() method adds to the GrectArray a full-filling width and height sequence of grects in the specified color.
        Not recommended with large grids (Slow performance).
        """

        for i in range(self.grid.gridHeight):
            for j in range(self.grid.gridWidth):
                self.array.append(list())
                self.array[i].append(Grect(self.grid, j, i, color, False))

    def draw(self, surface):

        """
        The draw() method draws the sequence of grects.
        It loops through the sequence and draws each grect in their own x and y positions.
        If the position of a grect is offset within the grid limits, it raises a TypeError.

        If the background boolean is false, it draws it as just a simple sequence.
        If the background boolean is true, it draws it as a full-filling background.

        """

        if self.isBackground == False:
            try:
                if len(self.array) > 0:
                    for i in self.array:
                        pygame.draw.rect(surface, i.color, (i.x, i.y, self.grid.gridRectWidth, self.grid.gridRectHeight))
            except:
                pass

        elif self.isBackground == True:
            try:
                if len(self.array) > 0:
                    for i in self.array:
                        for j in i:
                            pygame.draw.rect(surface, j.color, (j.x, j.y, self.grid.gridRectWidth, self.grid.gridRectHeight))
            except:
                pass

    def getGrid(self):

        """
        Returns the associated grid instance.
        """

        return self.grid

    def isBackground(self):

        """
        Returns the isBackground boolean.
        """

        return self.isBackground

class Controller():

    """
    A Pygrid controller alters the positions of grects.
    """

    def __init__(self, grid, isArray, isWarper):

        """
        The constructor defines four basic directions, and a boolean indicating if the controlled target is a sequence or not.
        The isWarper boolean indicates if the Controller shall allow screen-warping.
        """

        self.grid = grid
        self.UP = 0
        self.DOWN = 1
        self.LEFT = 2
        self.RIGHT = 3
        self.isArray = isArray
        self.isWarper = isWarper

    def control(self, target, direction, step):

        """
        The control() method moves the specified grect or grect sequence target in a specified direction, by specified step and in the controller's grid.
        """

        if self.isArray == False:
            if direction == 0:
                try:
                    target.y -= self.grid.getY(step)
                except:
                    pass
                if self.isWarper == True:
                    if target.y < self.grid.getY(0):
                        target.y = self.grid.getY(self.grid.gridHeight-1)
            elif direction == 1:
                try:
                    target.y += self.grid.getY(step)
                except:
                    pass
                if self.isWarper == True:
                    if target.y > self.grid.getY(self.grid.gridHeight-1):
                        target.y = self.grid.getY(0)
            elif direction == 2:
                try:
                    target.x -= self.grid.getX(step)
                except:
                    pass
                if self.isWarper == True:
                    if target.x < self.grid.getX(0):
                        target.x = self.grid.getX(self.grid.gridWidth-1)
            elif direction == 3:
                try:
                    target.x += self.grid.getX(step)
                except:
                    pass
                if self.isWarper == True:
                    if target.x > self.grid.getX(self.grid.gridWidth-1):
                        target.x = self.grid.getX(0)

        elif self.isArray == True:
            if len(target.array) > 0:
                for i in target.array:
                    if direction == 0:
                        try:
                            i.y -= self.grid.getY(step)
                        except:
                            pass
                        if self.isWarper == True:
                            if i.y < self.grid.getY(0):
                                i.y = self.grid.getY(self.grid.gridHeight-1)
                    elif direction == 1:
                        try:
                            i.y += self.grid.getY(step)
                        except:
                            pass
                        if self.isWarper == True:
                            if i.y > self.grid.getY(self.grid.gridHeight-1):
                                i.y = self.grid.getY(0)
                    elif direction == 2:
                        try:
                            i.x -= self.grid.getX(step)
                        except:
                            pass
                        if self.isWarper == True:
                            if i.x < self.grid.getX(0):
                                i.x = self.grid.getX(self.grid.gridWidth-1)
                    elif direction == 3:
                        try:
                            i.x += self.grid.getX(step)
                        except:
                            pass
                        if self.isWarper == True:
                            if i.x > self.grid.getX(self.grid.gridWidth-1):
                                i.x = self.grid.getX(0)

    def getGrid(self):

        """
        Returns the associated grid instance.
        """

        return self.grid

    def isArray(self):

        """
        Returns the isArray boolean.
        """

        return self.isArray

    def isWarper(self):

        """
        Returns the isWarper boolean.
        """

        return self.isWarper

main.py 1.1

"""

====================================================================

PYGRID ENGINE 1.1
Copyright (C) <2014>  <Ericson Willians.>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

====================================================================
Engine written by Ericson Willians, a brazilian composer and programmer.
XX
CONTACT: ericsonwrp@gmail.com
AS A COMPOSER: https://soundcloud.com/r-p-ericson-willians
YOUTUBE CHANNEL: http://www.youtube.com/user/poisonewein

====================================================================
"""

__author__ = 'EricsonWillians'

import pygame
import pygrid

if __name__ == "__main__":

    pygame.init()

    grid = pygrid.Grid([200, 200], [4, 4])
    speed = 32
    clock = pygame.time.Clock()
    done = False
    screen = pygame.display.set_mode((grid.gridWidthInPixels, grid.gridHeightInPixels))
    pygame.display.set_caption(str(grid.gridWidth) + " by " + str(grid.gridHeight) + " Pygrid (" + str(grid.gridRectWidth) + "px by " + str(grid.gridRectHeight) + "px)")

    def update():

		# Draw your grects here.
        pygame.display.update()
        pygame.display.flip()
        clock.tick(speed)

    while not done:
        keys = pygame.key.get_pressed()

        if keys[pygame.K_UP] or keys[pygame.K_w]:
            pass
        if keys[pygame.K_DOWN] or keys[pygame.K_s]:
            pass
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            pass
        if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            pass

        for e in pygame.event.get():
            if e.type == pygame.QUIT or keys[pygame.K_ESCAPE]:
                done = True

        update()

    quit()
Edited by EricsonWillians
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0