Piader v2 – the game

We have all  bricks to build a game view. It is the most important view in game 🙂

Game view

Lets think for a while about what we want in game view? We want enemy for sure. It will move randomly and drop a bomb. So we also need the bomb 🙂
Player would be nice as well. And as he shoots we need missile. These are four sprites and for them we will use mechanics from version 1.

We need to prepare a bare view. Create a file in view folder with name game.py. We will start with minimum code:

# -*- coding: utf-8 -*-

""" game view
"""

import abstract.view as view
import lcdmanager.widget.canvas as canvas #pylint: disable=I0011,F0401


class Game(view.View):
    """Game view"""
    def __init___(self, lcdmanager, game):
        self.manager = lcdmanager
        self.game = game
        self.canvas = canvas.Canvas(0, 0, lcdmanager.width, lcdmanager.height)
        self.manager.add_widget(self.canvas)

    def hide(self):
        """hide game tab"""
        self.pane.visibility = False

    def show(self):
        """show home tab"""
        self.pane.visibility = True

    def loop(self, action):
        pass

We need to do some changes to make this view usable. Open game.py add import and initialization of our new view. Of course we need to add loop calling in game_tab and in function set_tab add view hiding.
What is left is to hook show view event into start game button. Open home.py and replace old function with this:

    def _button_start(self, widget):
        """start game"""
        self.game.set_tab('game')

When you start a game you will be able to reach game view. But there is no way back yet 🙂

Sprites

We may now add sprites. As in version one we will start with enemy. But before that we need to move some code from game.py to game view. It is the code responsible for storing objects and player sprite.
In Game class delete object dictionary and player variable, we may safely remove whole init_game function.
We have another variable that is not necessary, self.size. We may directly check size in manager.
After all those changes new __init__ in Game class is like this:

    def __init__(self, game_manager, score_manager=None):
        """init class"""
        self.game_manager = game_manager
        self.score_manager = score_manager
        if self.game_manager.width < 6:
            raise ValueError("Width must be larger than 5")
        if self.game_manager.height < 4:
            raise ValueError("Height must be larger than 3")
        self.cfg = cfg.Configuration()
        self.queue = Queue.Queue()
        self.event_server = event_server.EventServerThread(self.queue)
        self.local_keyboard = local_key.Keyboard()
        self.views['home'] = home_view.Home(self.game_manager, self)
        self.views['options'] = options_view.Options(self.game_manager, self)
        self.views['game'] = game_view.Game(self.game_manager, self)

We may move to game view. When we call function show game needs to reset itself. We need to create first enemy, player and empty object dictionary. Refactored game view:

# -*- coding: utf-8 -*-

""" game view
"""

import abstract.view as view
import lcdmanager.widget.canvas as canvas #pylint: disable=I0011,F0401
import player
import enemy


class Game(view.View):
    """Game view"""
    player = None

    def __init__(self, lcdmanager, game):
        self._options = {
            'objects': []
        }
        self.manager = lcdmanager
        self.game = game
        self.canvas = canvas.Canvas(0, 0, lcdmanager.width, lcdmanager.height)
        self.manager.add_widget(self.canvas)

    def hide(self):
        """hide game tab"""
        self.canvas.visibility = False

    def show(self):
        """show game tab"""
        self._options['objects'] = []
        self.player = player.Player(
            (self.canvas.width / 2) - 2,
            self.canvas.height - 1,
            self.canvas.width,
            self._options['objects']
        )       
        self._options['objects'].append(
            enemy.Enemy(2, 0, self.canvas.width, self._options['objects'])
        )
        self._options['objects'].append(self.player)
        self.canvas.visibility = True

    def loop(self, action):
        """game tick"""
        pass

Lets add enemy and its movement, we will use AI from previous version. It was simple and naive, randomly move left or right and randomly drop a bomb.
I copied the code from v1 (function draw and part of tick), replaced self.game_lcd with self.canvas and it just works. Perfect.

    def loop(self, action):
        """game tick"""
        self.canvas.clear()
        for item in self._options['objects']:
            item.tick()
            self.draw(item)

    def draw(self, item):
        """draw sprite on screen"""
        (position_x, position_y) = item.get_position()

        if position_y >= self.canvas.height or position_y < 0:
            self._options['objects'].remove(item)
            item.event_discard()
            return

        self.canvas.write(item.get_sprite(), position_x, position_y)

But what about player? With six lines we have once again ability to move and fire, add them before self.canvas.clear():

        if action == 'move.left':
            self.player.move_left()
        if action == 'move.right':
            self.player.move_right()
        if action == 'action':
            self.player.fire()

What is left is collision detection. We will once again copy code from previous version. It’s almost working, what we need to change is player getting hit. In old version it was game over instantly but now it will subtract lives and display game over when it reach 0.
Another change is to add Game Over view.
Lets start with lives. We need some live counter add ‘lives’: 0 to self._options in __init__ and if you are there add score key as well.
We need to add initialization to show function:

self._options['lives'] = self.game.cfg.lives
self._options['score'] = 0

And another function to handle player hit:

    def player_hit(self, source, target):
        """player is hit"""
        source.event_discard()
        self._options['lives'] -= 1
        if self._options['lives'] <= 0:
            self.game.set_tab('gameover')

After starting game almost everything works 🙂 First problem is when player gets a hit. Something is wrong with bombs. Second problem is that after losing all lives game crashes.
bug

 

 

 

 

We can easily fix second problem by adding game over view. We will just display string and after any event return to home view. Create new file gameover.py in views  and copy:

# -*- coding: utf-8 -*-

""" game over view
"""

import abstract.view as view
import lcdmanager.widget.pane as pane #pylint: disable=I0011,F0401
import lcdmanager.widget.label as label #pylint: disable=I0011,F0401


class Gameover(view.View):
    """Game Over view"""
    def __init__(self, lcdmanager, game):
        """create all widgets"""
        self.manager = lcdmanager
        self.pane = pane.Pane(0, 0, 'gameover')
        self.pane.width = lcdmanager.width
        self.pane.height = lcdmanager.height
        self.game = game
        title = label.Label(self.pane.width / 2 - 5, self.pane.height / 2, 'title')
        title.label = "Game Over"
        self.pane.add_widget(title)

        self.manager.add_widget(self.pane)

    def hide(self):
        """hide tab"""
        self.pane.visibility = False

    def show(self):
        """show tab"""
        self.pane.visibility = True

    def loop(self, action):
        """tick"""
        if action != None:
            self.game.set_tab('home')

We need to add this view to Game class. So import it and initialize (similar to previous views). We will do some refactoring now. See that we have very simple functions in game class to call loop on active view. We can make it much simpler. Remember nice amount of if’s ? Replace them with:

self.views[self.option['gui_current_tab']].loop(action)

Delete unused functions. Next go to set_tab function. We are hiding all views but we were doing it manually. So for each view we had to add another line. Time to change it and add some loop:

    def set_tab(self, tab):
        """change views"""
        for view in self.views:
            self.views[view].hide()
        self.views[tab].show()
        self.option['gui_current_tab'] = tab

After all refactoring we have a working game with only one bug 🙂 First change is in Enemy class move self.bombs to __init__. And second change is to remove source.event_discard() from player_hit function.
After few moments I found another bug. Player can go out of screen to the right. Fix it by yourself or look into the code.

Stop! Jenkins time!

Not much here, unused parameters that could be removed, too long line and too few public methods in configuration class.

Summary

We now have a game view and game mechanic. DMO (our enemy) can move and drop bombs, the player can move and return fire.
We also have a game over view. And finally we can play our little game.
But this is not the end, we don’t have a scoreboard and difficulty level is ignored.

Download Piader

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s