General 2d game design book

  • (3 Pages)
  • +
  • 1
  • 2
  • 3

42 Replies - 4063 Views - Last Post: Yesterday, 12:59 AM Rate Topic: -----

#1 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

General 2d game design book

Posted 21 July 2019 - 03:40 AM

Hi everyone!
I'm looking for a book about basic 2d game design. I've just started a CS degree and looking to supplement my studying by filling a huge gap in my skill set: games. I'm comfortable in Python and can get by in Javascript, but I don't really mind what language the book is based in as I'm looking for something more general.
Ultimately, I'm looking for something that will teach me how to code things like snake, chess, Tetris.

However, I don't necessarily want a tutorial how to make these games, but more the knowledge of why the data structures are chosen, so in the future I can make those design choices for other games I want to code. Hope that makes sense.

So I guess, what I'm looking for is a general book about 2d game design featuring info about data structures, game loops etc so that I can feel comfortable coding something like Tetris.

Any suggestions greatly appreciated!

Is This A Good Question/Topic? 0
  • +

Replies To: General 2d game design book

#2 modi123_1   User is offline

  • Suitor #2
  • member icon



Reputation: 15175
  • View blog
  • Posts: 60,742
  • Joined: 12-June 08

Re: General 2d game design book

Posted 21 July 2019 - 08:55 AM

I would suggest picking up a, recently printed and fairly high reviewed, book on Unity that is 2d specific. There's not just "one way" of doing 2d programming so it's best to pair concepts with the ability to implement in a game engine.

Example:
https://www.amazon.c...g/dp/1484237714
Was This Post Helpful? 0
  • +
  • -

#3 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

Re: General 2d game design book

Posted 22 July 2019 - 10:23 AM

Awesome.. thanks.. will check that out.

Also, I was thinking, maybe I'm being too dismissive of those tutorial type "build tetris" videos.. maybe there is some merit in following a few different ones and seeing how they compare and what is different, what is the same.. I was thinking it would be too much "hand-holding" and I wouldn't get a good knowledge of the design process behind the steps, but maybe I would once I did a few..
Was This Post Helpful? 0
  • +
  • -

#4 modi123_1   User is offline

  • Suitor #2
  • member icon



Reputation: 15175
  • View blog
  • Posts: 60,742
  • Joined: 12-June 08

Re: General 2d game design book

Posted 22 July 2019 - 10:28 AM

Perhaps, but I opt to frequently go for structured learning first then, with concepts in hand, check out videos.
Was This Post Helpful? 0
  • +
  • -

#5 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

Re: General 2d game design book

Posted 22 July 2019 - 12:22 PM

View Postmodi123_1, on 22 July 2019 - 10:28 AM, said:

Perhaps, but I opt to frequently go for structured learning first then, with concepts in hand, check out videos.

Ok.. will do that.. thanks again!
Was This Post Helpful? 0
  • +
  • -

#6 NeoTifa   User is offline

  • NeoTifa Codebreaker, the Scourge of Devtester
  • member icon





Reputation: 4532
  • View blog
  • Posts: 19,148
  • Joined: 24-September 08

Re: General 2d game design book

Posted 23 July 2019 - 11:44 AM

Thanks for being open to those small games tutorials. Most just scoff and then ask how to make WoW. Were you wanting to make your own engine or just doing it in general? I think learning how to make small games, engine included, has merit.
Was This Post Helpful? 0
  • +
  • -

#7 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

Re: General 2d game design book

Posted 25 July 2019 - 02:41 AM

I'd like to design an engine someday yes, but for now what I really want is to bridge the gap between my knowledge of programming concepts (OOP, arrays, conditional loops etc) and simple games. For example, the idea that a Tetris piece is an array that rotates blows my mind.. I didn't even think an array could rotate ! I'd always just thought of it as a long list.. So I need to grasp some basic core abstractions about game design..

I've recently finished the Nand2Tetris courses and I struggled when it game to designing your own game as I'm missing these fundamental design structures..

My plan is to learn how to design something like snake first.. then Tetris.. then chess.. then maybe a level of super mario.. and then I'd love to make a racing game.
My ultimate goal is to be able to design all these games in pseudo-code without needing to look up anything first..
Was This Post Helpful? 0
  • +
  • -

#8 NeoTifa   User is offline

  • NeoTifa Codebreaker, the Scourge of Devtester
  • member icon





Reputation: 4532
  • View blog
  • Posts: 19,148
  • Joined: 24-September 08

Re: General 2d game design book

Posted 25 July 2019 - 07:44 AM

Well those technically are engines you made yourself unless you used Unity or UE.
Was This Post Helpful? 0
  • +
  • -

#9 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

Re: General 2d game design book

Posted 25 July 2019 - 07:59 AM

Ah ok then yes, I want to build my own engines for these small projects. (For some reason I was thinking engine meant something bigger and flashier than I was planning)
Was This Post Helpful? 0
  • +
  • -

#10 astonecipher   User is offline

  • Senior Systems Engineer
  • member icon

Reputation: 2906
  • View blog
  • Posts: 11,328
  • Joined: 03-December 12

Re: General 2d game design book

Posted 25 July 2019 - 11:32 AM

An engine is just a driver. What you are probably thinking of is more like Unity, where the engine controls lighting, physics, and all that, but an engine can just be the movement of a game sprite.
Was This Post Helpful? 2
  • +
  • -

#11 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

Re: General 2d game design book

Posted 27 July 2019 - 11:33 AM

Well been doing loads of reading this week and decided to test myself to see if I could code the first game on my "wishlist" which was snake with very little looking up of other's code.. And after about 6 hours solid coding today, I was able to do it! Not the best implementation I feel... I don't think I should have made it OOP, and I'm not sure my implementation of the speed up is good.. as if I press the keys quickly I can make go back on itself. I think my title bar class was over the top too for such a small thing and could be tidied up by putting it together with the score and new_game message maybe. But overall very happy I was able to make something like this. A week ago I wouldn't have known where to start!

Any critiques greatly appreciated!

import pygame, sys, random                  # sys for closing programm.  random for Apple position
from pygame.locals import *                 # for using pygame keywords like QUIT or KEYDOWN

pygame.init()                               # Initialise pygame
pygame.display.set_caption("Snake")         # Title bar of window

starting_FPS = 5                            # Starting FPS used for resetting the game
FPS = starting_FPS                          # FPS  - this speeds up as game progresses
fpsClock = pygame.time.Clock()              # Clock object

# =========== CONSTANTS =========== #
SCOREBAR = 50                               # Score bar height bar
SCREEN_WIDTH  = 400                         # Playing screen width in pixels
SCREEN_HEIGHT = 400                         # Playing screen height in pixels
WINDOW_SIZE   = (SCREEN_WIDTH, SCREEN_HEIGHT + SCOREBAR)    # Tuple of window size()
GRID_SIZE     = 10                          # Grid size in pixels
GRID_MID_Y = SCREEN_HEIGHT / 2 / GRID_SIZE  # Mid point on y axis in Grid coordinates
GRID_MID_X = SCREEN_WIDTH / 2 / GRID_SIZE   # Mid point on x axis in Grid coordinates
GRID_WIDTH = SCREEN_WIDTH / GRID_SIZE       # Total number of grid squares on x axis
GRID_HEIGHT = SCREEN_HEIGHT / GRID_SIZE     # Total number of grid squares on y axis

assert(SCREEN_WIDTH % GRID_SIZE == 0)       # Confirm that SCREEN_WIDTH is divisible by GRID_SIZE
assert(SCREEN_HEIGHT % GRID_SIZE == 0)      # Confirm that SCREEN_HEIGHT is divisible by GRID_SIZE

# =========== COLOURS =========== #
BLACK = (  0,  0,  0)
WHITE = (255,255,255)
GREY = (155,155,155)
RED   = (255,  0,  0)

BG_COLOR = GREY

x = "x"
y = "y"
UP = "up"
DOWN = "down"
LEFT = "left"
RIGHT = "right"



DISPLAYSURF = pygame.display.set_mode(WINDOW_SIZE)      # Create display surface and set to WINDOW_SIZE
DISPLAYSURF.fill(BG_COLOR)                              # Fill window with background colour


class Main:
    def __init__(self):
        Game()

class Game:

    def __init__(self):
        """ Initialises the new game with score of 0 and then calls the run method """
        self.score = 0
        self.run()

    def gameover(self):
        """ Checks for quit, or space bar which starts a new game. """
        while True:
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == KEYDOWN:
                    if event.key == K_SPACE:
                        DISPLAYSURF.fill(BG_COLOR)  # Fill window with background colour to clear last game
                        Main()                  # Restart game

            self.new_game_msg()
            pygame.display.update()
            fpsClock.tick(FPS)

    def new_game_msg(self):
        """ Displays message showing how to start new game """
        fontObj = pygame.font.Font("freesansbold.ttf",22)
        new_game_surface = fontObj.render("Press SPACE to play again", True, BLACK)
        new_game_rect = new_game_surface.get_rect()
        new_game_rect.center = (SCREEN_WIDTH / 2,SCREEN_HEIGHT /2)
        DISPLAYSURF.blit(new_game_surface, new_game_rect)


    def display_score(self):
        """ Displays the score onto the bottom of the window where the title bar is """
        fontObj = pygame.font.Font("freesansbold.ttf",32)
        score_surface = fontObj.render("Score: {}".format(self.score), True, BLACK)
        score_rect = score_surface.get_rect()
        score_rect.center = (SCREEN_WIDTH / 2,SCREEN_HEIGHT + (SCOREBAR / 2))
        DISPLAYSURF.blit(score_surface, score_rect)

    def run(self):
        """ Initialises the title bar, calls display_score method and initialises a new snake and a new apple """

        global FPS              # Global used to speed up game after every apple eaten
        title = Title()         # Initialise title bar
        self.display_score()    # Display score
        snake = Snake()         # Initialise new snake
        apple = Apple()         # Initialise new apple

        ##### Game loop #####
        while True:
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()

                # Arrow controls direction, and checks to not head directly to direction it came from
                elif event.type == KEYDOWN:
                    if event.key == K_LEFT and snake.get_direction() != RIGHT:
                        snake.set_direction(LEFT)
                    elif event.key == K_RIGHT and snake.get_direction() != LEFT:
                        snake.set_direction(RIGHT)
                    elif event.key == K_UP and snake.get_direction() != DOWN:
                        snake.set_direction(UP)
                    elif event.key == K_DOWN and snake.get_direction() != UP:
                        snake.set_direction(DOWN)

            if not snake.move():        # if snake can't move, call gameover, else move.
                FPS = starting_FPS
                self.gameover()

            if snake.eating(apple.x, apple.y):
                apple = Apple()
                self.score += 1
                title.draw()
                self.display_score()
                snake.grow()
                FPS += 1       # Speed up game after every apple eaten

            pygame.display.update()
            fpsClock.tick(FPS)


class Snake:
    HEAD = 0

    def __init__(self):
        """ Initialises new snake with colour of black, and a 3 block body.
            Direction set to left, """
        self.color = BLACK
        self.body = [{x: GRID_MID_X + 0, y: GRID_MID_Y},
                     {x: GRID_MID_X + 1, y: GRID_MID_Y},
                     {x: GRID_MID_X + 2, y: GRID_MID_Y}]
        self.direction_x = -1
        self.direction_y = 0

    def draw(self):
        """ Draws snake on display surface """
        for block in self.body:
            block_position = (block[x] * GRID_SIZE, block[y] * GRID_SIZE, GRID_SIZE, GRID_SIZE)
            pygame.draw.rect(DISPLAYSURF, self.color, block_position)

    def erase(self):
        """ Erases snake from display surface """
        for block in self.body:
            block_position = (block[x] * GRID_SIZE, block[y] * GRID_SIZE, GRID_SIZE, GRID_SIZE)
            pygame.draw.rect(DISPLAYSURF, BG_COLOR, block_position)

    def move(self):
        """ Erases snake from display surface, deletes last tail position and adds new head position in direction
            heading.  Then draws snake in new position.
            Calculates if it's outside the screen, or hit its own tail and if so: returns False, else returns True """
        self.erase()
        self.body.pop()
        new_head = [{x: self.body[Snake.HEAD][x] + self.direction_x, y: self.body[Snake.HEAD][y] + self.direction_y}]
        self.body = new_head + self.body
        self.draw()
        if self.body[Snake.HEAD][x] < 0 or self.body[Snake.HEAD][x] == GRID_WIDTH:
            self.stop()
            return False
        if self.body[Snake.HEAD][y] < 0 or self.body[Snake.HEAD][y] == GRID_HEIGHT:
            self.stop()
            return False
        if self.hit_body():
            return False
        return True

    def stop(self):
        """ Stops snake by setting it's X and Y direction to 0 """
        self.direction_x = 0
        self.direction_y = 0

    def set_direction(self, dir):
        """ Sets its direction to given argument of one of:
            UP, DOWN, RIGHT, LEFT """
        if dir == UP:
            self.direction_x = 0
            self.direction_y = -1
        elif dir == DOWN:
            self.direction_x = 0
            self.direction_y = 1
        elif dir == RIGHT:
            self.direction_x = 1
            self.direction_y = 0
        elif dir == LEFT:
            self.direction_x = -1
            self.direction_y = 0

    def get_direction(self):
        """ Returns the direction the snake is headed """
        if self.direction_x == 1:
            return RIGHT
        elif self.direction_x == -1:
            return  LEFT
        elif self.direction_y == -1:
            return UP
        elif self.direction_y == 1:
            return DOWN

    def eating(self, apple_x, apple_y):
        """ Returns True if snake head has hit the apple """
        if self.body[Snake.HEAD][x] == apple_x and self.body[Snake.HEAD][y] == apple_y:
            return True

    def grow(self):
        """ Increases its body size by one """
        new_head = [{x: self.body[Snake.HEAD][x] + self.direction_x, y: self.body[Snake.HEAD][y] + self.direction_y}]
        self.body = new_head + self.body

    def hit_body(self):
        """ Returns True if head hits its own body, else returns False """
        for block in self.body[1:]:
            if block[x] == self.body[Snake.HEAD][x] and block[y] == self.body[Snake.HEAD][y]:
                return True
        else:
            return False

class Apple:
    count = 0         # Apple count keeping track of how many apples have been initialised in the current game
    def __init__(self):
        """ Initialises new apple with colour of red, at a random x, y coordinate and adds 1 to the count.
            then draws to display surface """
        self.color = RED
        self.x = random.randint(0, GRID_WIDTH - 1)
        self.y = random.randint(0, GRID_HEIGHT - 1)
        Apple.count += 1
        self.draw()

    def draw(self):
        """ Draws apple to the display surface """
        block_position = (self.x * GRID_SIZE, self.y * GRID_SIZE, GRID_SIZE, GRID_SIZE)
        pygame.draw.rect(DISPLAYSURF, self.color, block_position)


class Title:
    def __init__(self):
        """ Initialises new title bar at bottom of window with colour white, then draws to display surface """
        self.bg = WHITE
        self.position = (0, SCREEN_HEIGHT, SCREEN_WIDTH, SCOREBAR)
        self.draw()

    def draw(self):
        """ Draws title bar on display surface """
        pygame.draw.rect(DISPLAYSURF, self.bg, self.position)


if __name__ == "__main__":
    Main()

Was This Post Helpful? 2
  • +
  • -

#12 LadyCynthia   User is offline

  • D.I.C Head

Reputation: 17
  • View blog
  • Posts: 77
  • Joined: 29-December 16

Re: General 2d game design book

Posted 05 August 2019 - 06:38 PM

You could check out Web Game Developer's Cookbook by Evan Burchard.It goes through a wide variety of games of increasing complexity.


https://www.amazon.c...=gateway&sr=8-1
Was This Post Helpful? 1
  • +
  • -

#13 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7468
  • View blog
  • Posts: 15,483
  • Joined: 16-October 07

Re: General 2d game design book

Posted 06 August 2019 - 02:28 AM

Is this really your first stab at this? Good job.

There is a mix up of good ideas and bad ideas here, owing probably some internet copy paste?

You're right, the pseudo OOP part is not good. If your class runs itself, it's time to think about why you have it.

For example, this:
class Title:
    def __init__(self):
        """ Initialises new title bar at bottom of window with colour white, then draws to display surface """
        self.bg = WHITE
        self.position = (0, SCREEN_HEIGHT, SCREEN_WIDTH, SCOREBAR)
        self.draw()

    def draw(self):
        """ Draws title bar on display surface """
        pygame.draw.rect(DISPLAYSURF, self.bg, self.position)



Could, and should, be this:
def draw_title():
    pygame.draw.rect(DISPLAYSURF, WHITE, (0, SCREEN_HEIGHT, SCREEN_WIDTH, SCOREBAR))



The point of a class, or rather a class instance, is to store state. While you've technically initialized a state of bg and position; do they ever change? What advantage does such an object hold over a simple function?

Along the same lines, your Main "object" is pointless, you could simply invoke Game. I applaud your heavy use of comments, I think. Did they help?

Your Apple is interesting. You're using a class level variable to store the count. You're still not really doing anything that justifies the existence of an instance, though. I'd store the apple count in Game and use these functions:
def rand_apple():
    return random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)

def draw_apple(apple):
    x, y = apple
    pygame.draw.rect(DISPLAYSURF, RED, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE))



Most of your classes needn't be. With the notable exception of Snake, which really benefits from keeping that logic local; though self.color is silly.

Overall, though, excellent job. I'm half tempted to refactor this to minimal classes. However, I'll leave that as an exercise for you.
Was This Post Helpful? 2
  • +
  • -

#14 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7468
  • View blog
  • Posts: 15,483
  • Joined: 16-October 07

Re: General 2d game design book

Posted 06 August 2019 - 04:17 AM

Hmm... just spotted this:
Main()                  # Restart game


No, wrong, don't do that. Don't ever do that.

Your game loop should, well, loop. If it's in a game over state, then nothing is moving about. The game is restarted by setting the game state variables to their initial values again.

Hope this helps.
Was This Post Helpful? 2
  • +
  • -

#15 mrdman   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 20
  • Joined: 21-July 19

Re: General 2d game design book

Posted 13 August 2019 - 04:39 AM

Firstly, thanks so much for taking the time to give me your feedback, I appreciate it so much!

Quote

Is this really your first stab at this? Good job.


Yep, first time coding a game.. Up to now, I've only ever built little python tools (scraping / inventory app with tkinter) alongside any exercises in books etc.

Quote

There is a mix up of good ideas and bad ideas here, owing probably some internet copy paste?

No copying and pasting.. just my bad design and overall bad vision / structure! I'm glad you picked up on this actually, as I often feel like my code starts well and elegant but then before long it turns into a mess of different ideas and ways to do things.. Will this just come with more experience? Or some recommended reading?

Quote

The point of a class, or rather a class instance, is to store state. While you've technically initialized a state of bg and position; do they ever change? What advantage does such an object hold over a simple function?

Ah that's a good way to think about it.. One thing that my books don't really teach is the underlying point of a class, just how to make them! I think that's one reason my code seems all over the place too, the knowledge about when / when not to use things.

Just after posting the game and before reading all these comments I actually implemented a version with separate class files, which thinking about it now was probably wrong in this instance for a simple game.. but I wanted to get to grips with separating classes and then importing them etc. But a lot of the problems you speak about still will be there.. so I'll digest all you've wrote and rework them into simpler classes.

Where you say never do Main()... do you mean I should have done Game() instead to initialise another game? Or I should have a reset method and then do game.reset(); game.run() ?



Last weekend I also undertook the second project that was on my wish list (tetris).. this time not looking up anyone elses code at all.. and although I got a working game, I missed something big I feel in the way I coded it, because I'm operating at 5fps to get the effect I want.. but that means that y arrow movements are also 5fps which feels wrong. Need a rethink there. Should I post that here too? To make this sort of my journey through 2D gaming? (My next one is chess which I still feel I have no idea how to tackle!)
Was This Post Helpful? 0
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3