Skip to navigation
Logo Penaz's Area

cat /dev/random > penaz

Pygame: Primitive Shooter II !!


Poco tempo dopo aver programmato Primitive Shooter, ecco Primitive Shooter II, una riprogrammazione che mi ha permesso di aggiungere alcune caratteristiche, sempre usando Pygame e Python.

Buongiorno, dopo poco tempo che ho creato Primitive Shooter, mi sono reso conto che la struttura del codice era poco pulita e non mi permetteva di fare le aggiunte che volevo. Perciò ho deciso di riprendere in mano Python e Pygame e di riprogrammare il tutto. Il risultato è Primitive Shooter II: un po' meno grezzo del primo, ma sempre spigoloso come un rettangolo.

Ecco i pochi moduli che lo compongono:

Modulo Descrizione
Classe Player() La classe che descrive le caratteristiche del giocatore
Classe Enemy() Il classico nemico rosso
Classe Bullet() Un normale proiettile
Classe BigBullet() Un Grosso Proiettile
Classe PowerUp() Un serie di potenziatori diversi
Funzione Shoot() Controlla il cannone e la sua potenza

Oltre a questi vi è anche una classe esterna "Vector" per la gestione dei movimenti, lunga 65 righe di codice, che si vanno ad aggiungere alle 300 giuste giuste del gioco.

Vediamo le caratteristiche:

  • Supporto per nuovi tipi di powerup: potenza fuoco, velocità fuoco, velocità nave
  • Implementazione di macchine a stati
  • Persa la schermata di game over e il supporto per le vite (devo ancora programmarlo)
  • Implementato una specie di "beam" alla R-Type.
  • Grafica quadrettosa come il suo predecessore

Se volete scaricare il gioco dovete prima scaricare la classe Vector, presente nella prossima pagina e poi il file game.py ed inserirli nella stessa cartella. Per provare il gioco basta dare il comando:

python Game.py

Anche questo pezzo di codice è distribuito con la sola clausola "Attribuzione"

Diamo un'occhiata al codice della classe Vector:

#!/usr/bin/env python
from math import sin,cos,sqrt
class Vector(object):
    "'Classe che permette la creazione e gestione dei vettori'"
    def __init__(self,x=0.,y=0.):
        "'Metodo di Inizializzazione, in assenza di dati, inizializza il vettore a zero'"
        self.x=x
        self.y=y
    "'Crea un vettore per differenza di punti'"
    @staticmethod
    def FromPoints(a,b):
        "'FromPoints(int,int) -> Vector'"
        return Vector(b[0]-a[0],b[1]-a[1])
    "'Restituisce il Prodotto Scalare di due vettori'"
    def Scalar(self,b):
        "'Scalar(Vector,Vector) -> float)'"
        return self.x*b.x+self.y*b.y
    "'Restituisce il Modulo di un vettore'"
    def Mod(self):
        "'Mod(Vector) -> float'"
        return sqrt(self.Scalar(self))
    "'Normalizza il vettore'"
    def Normalize(self):
        "'Vector.Normalize -> Self'"
        mod=self.Mod()
        if mod>0:
            self.x/=mod
            self.y/=mod
        return
    "'Crea un vettore, dato un angolo, restituisce un versore'"
    @staticmethod
    def FromAngle (theta):
        "'FromAngle(theta) -> Vector'"
        v=Vector(cos(theta),sin(theta))
        v.Normalize()
        x=v.x
        y=v.y
        del v
        return Vector(x,y)
    def __neg__(self):
        "'Vector(a,b) -> Vector(-a,-b)'"
        return (-self.x, -self.y)
        def __add__ (self,vec2):
        "'Vector(a,b)+Vector(c,d) -> Vector(a+c,b+d)'"
        return Vector(self.x + vec2.x, self.y+vec2.y)
        def __str__(self):
        "'Vector(a,b) -> Str(a,b)'"
        return "(%s,%s)"%(self.x,self.y)
        def __sub__(self,vec2):
        "'Vector(a,b)-Vector(c,d) -> Vector(a-c,b-d)'"
        return Vector(self.x-vec2.x, self.y-vec2.y)
    def __mul__(self,scal):
        "'Vector(a,b) * int(k) -> Vector(k*a,k*b)'"
        return Vector(self.x*scal, self.y*scal)
    def __div__(self, scal):
        "'Vector(a,b) / int(k) -> Vector(a/k,b/k)'"
        return Vector(self.x/scal, self.y/scal)
    "'Converte il vettore in un tupla'"
    def to_tuple(self):
        "'Vector.to_tuple() -> (float,float)'"
        return (self.x,self.y)
    "'Arrotonda all'intero i valori del vettore'"
    def int(self):
        "'Vector.int() -> Self'"
        return (round(self.x),round(self.y))

Questa classe sarà richiamata solo quando necessario.

Vediamo ora il codice vero e proprio del gioco:

#!/usr/bin/env python
#Imports
import pygame
from pygame.locals import *
from sys import exit
import Vector
from Vector import *
from math import pi
from random import randint,random
#Dichiarazioni
tp=0
sht=False
time=0
render_list=pygame.sprite.Group()
enemy_list=pygame.sprite.Group()
bullet_list=pygame.sprite.Group()
pow_list=pygame.sprite.Group()
player_list=pygame.sprite.Group()
shtinterval=0.5
shlvl=1
charge=0
#Classi
class Player(pygame.sprite.Sprite):
    move_x=0
    move_y=0
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)
        self.image=pygame.Surface((20,20))
        self.image.fill((255,255,255))
        self.rect=self.image.get_rect()
        self.rect.x=x
        self.rect.y=y
        render_list.add(self)
        player_list.add(self)
    def update(self):
        if self.rect.x+self.move_x<=0 or self.rect.x+self.move_x>=620:
            self.move_x=0
        else:
            self.rect.x+=self.move_x
        if self.rect.y+self.move_y<=0 or self.rect.y+self.move_y>=460:
            self.move_y=0
        else:
            self.rect.y+=self.move_y
#---------------------------------------------------------
class Enemy(pygame.sprite.Sprite):
    state="flying"
    waittime=0
    direction=Vector()
    move_x=0
    move_y=0
    speed=100
    d=0
    endwait=False
    def __init__(self,x,y):
        self.d=randint(0,1)
        pygame.sprite.Sprite.__init__(self)
        self.image=pygame.Surface((20,20))
        self.image.fill((255,0,0))
        self.rect=self.image.get_rect()
        self.rect.x=x
        self.rect.y=y
        render_list.add(self)
        enemy_list.add(self)
        self.direction=Vector.FromAngle((random()*(pi/2))+pi/4)
        self.direction.Normalize()
        self.move_x=self.direction.to_tuple()[0]
        self.move_y=self.direction.to_tuple()[1]
    def update(self):
        if self.state=="flying":
            if self.rect.x>=20 and self.rect.x<=620:
                self.rect.x+=self.move_x*(tp/1000.)*self.speed
            else:
                self.move_x=(-self.move_x)
                self.rect.x+=self.move_x*(tp/1000.)*self.speed
            self.rect.y+=self.move_y*(tp/1000.)*self.speed
            if self.rect.y in range(220,225):
                self.state="waiting"
        elif self.state=="waiting" and not self.endwait:
            if self.waittime>=2:
                self.direction=Vector.FromAngle(random()*3*pi/4)
                self.direction.Normalize()
                self.move_x=self.direction.to_tuple()[0]
                self.move_y=self.direction.to_tuple()[1]
                self.state="flying"
                self.endwait=True
            else:
                if self.d:
                    if self.rect.x+self.move_x<=620:
                        self.rect.x+=(tp/1000.)*self.speed
                    else:
                        self.move_x=0
                        self.d=0
                else:
                    if self.rect.x-self.move_x>=20:
                        self.rect.x-=(tp/1000.)*self.speed
                    else:
                        self.move_x=0
                        self.d=1
                self.waittime+=tp/1000.
        else:
            self.state="flying"
        collide=pygame.sprite.spritecollide(self,bullet_list,True)
        if collide:
            n=randint(0,3)
            if n==1:
                PowerUp(self.rect.x+self.rect.width/2,self.rect.y)
            self.kill()
#----------------------------------------------------------
class Bullet(pygame.sprite.Sprite):
    dir_x=0
    dir_y=0
    speed=500
    def __init__(self,x,y,theta,mirror=False):
        pygame.sprite.Sprite.__init__(self)
        self.image=pygame.Surface((2,10))
        self.image.fill((255,74,42))
        self.rect=self.image.get_rect()
        self.rect.x=x
        self.rect.y=y
        self.dir_x,self.dir_y=Vector.FromAngle(theta).to_tuple()
        if mirror:
            self.dir_x*=(-1)
        bullet_list.add(self)
        render_list.add(self)
    def update(self):
        self.rect.x+=self.dir_x*(tp/1000.)*self.speed
        self.rect.y+=self.dir_y*(tp/1000.)*self.speed
        if self.rect.y<=-20:
            self.kill()
class BigBullet(pygame.sprite.Sprite):
    speed=200
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)
        self.image=pygame.Surface((50,50))
        self.image.fill((255,74,42))
        self.rect=self.image.get_rect()
        self.rect.x=x
        self.rect.y=y
        bullet_list.add(self)
        render_list.add(self)
    def update(self):
        self.rect.y-=tp/1000.*self.speed
        if self.rect.y<=100:
            self.kill()
#----------------------------------------------------------
class PowerUp(pygame.sprite.Sprite):
    powtype=0
    speed=50
    def __init__(self,x,y):
        self.powtype=randint(1,2)
        pygame.sprite.Sprite.__init__(self)
        if self.powtype==1:
            self.image=PowTxt.render(" S ",True,(255,255,0),(255,0,0))
        elif self.powtype==2:
            self.image=PowTxt.render(" F ",True,(27,42,255),(255,128,0))
        self.rect=self.image.get_rect()
        self.rect.x=x
        self.rect.y=y
        render_list.add(self)
        pow_list.add(self)
    def update(self):
        self.rect.y+=(tp/1000.)*self.speed
        collide=pygame.sprite.spritecollide(self,player_list,False)
        if collide:
            if self.powtype==1:
                global shlvl
                shlvl+=1
            elif self.powtype==2:
                global shtinterval
                shtinterval-=0.1
            self.kill()
#----------------------------------------------------------
#Funzioni
#----------------------------------------------------------
def Shoot():
    global charge
    if charge==100 and bool(pygame.key.get_pressed()[122]):
        BigBullet(player.rect.x,player.rect.y-50)
        global charge
        charge=0
    if time>=shtinterval and bool(pygame.key.get_pressed()[122]):
        global sht
        sht=True
        time=0
        charge=0
    else:
        global sht
        global time
        sht=False
        time+=tp/1000.
    if sht:
        global shlvl
        if shlvl==1:
            Bullet(player.rect.x+player.rect.width/2,player.rect.y-10,-pi/2)
        elif shlvl==2:
            Bullet(player.rect.x,player.rect.y-10,-pi/2)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-pi/2)
        elif shlvl==3:
            Bullet(player.rect.x,player.rect.y-10,-pi/2)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-pi/2)
            Bullet(player.rect.x+player.rect.width/2,player.rect.y-15,-pi/2)
        elif shlvl==4:
            Bullet(player.rect.x+player.rect.width/2,player.rect.y-15,-pi/2)
            Bullet(player.rect.x,player.rect.y-10,-4*pi/9,True)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-4*pi/9)
        elif shlvl==5:
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-pi/2)
            Bullet(player.rect.x,player.rect.y-10,-4*pi/9,True)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-4*pi/9)
            Bullet(player.rect.x,player.rect.y-10,-pi/2)
        elif shlvl==6:
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-pi/2)
            Bullet(player.rect.x,player.rect.y-10,-4*pi/9,True)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-4*pi/9)
            Bullet(player.rect.x,player.rect.y-10,-pi/2)
            Bullet(player.rect.x+player.rect.width/2,player.rect.y-15,-pi/2)
        elif shlvl==7:
            Bullet(player.rect.x+player.rect.width/2,player.rect.y-15,-pi/2)
            Bullet(player.rect.x,player.rect.y-10,-4*pi/9,True)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-4*pi/9)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-2*pi/3,True)
            Bullet(player.rect.x,player.rect.y-10,-2*pi/3)
        elif shlvl==8:
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-pi/2)
            Bullet(player.rect.x,player.rect.y-10,-pi/2)
            Bullet(player.rect.x,player.rect.y-10,-4*pi/9,True)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-4*pi/9)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-2*pi/3,True)
            Bullet(player.rect.x,player.rect.y-10,-2*pi/3)
        elif shlvl==9:
            Bullet(player.rect.x+player.rect.width/2,player.rect.y-15,-pi/2)
            Bullet(player.rect.x+player.rect.width, player.rect.y-10, -pi/2)
            Bullet(player.rect.x,player.rect.y-10, -pi/2)
            Bullet(player.rect.x,player.rect.y-10,-4*pi/9,True)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-4*pi/9)
            Bullet(player.rect.x+player.rect.width,player.rect.y-10,-2*pi/3,True)
            Bullet(player.rect.x,player.rect.y-10,-2*pi/3)

        else:
            shlvl=9
    else:
        if charge<100:
            global charge
            charge+=(tp/1000.)*20
        else:
            charge=100
#----------------------------------------------------------
#Inizio Programma
#----------------------------------------------------------
pygame.init()
screen=pygame.display.set_mode((640,480),0,32)
pygame.display.set_caption("Primitive Shooter II!!!")
clock=pygame.time.Clock()
player=Player(320,455)
PowTxt=pygame.font.SysFont("Arial",10)
for n in xrange(0,20):
    Enemy(randint(20,620),randint(-5000,-500))
while True:
    for event in pygame.event.get():
        if event.type==QUIT:
            exit()
        if event.type==KEYDOWN:
            if event.key==K_LEFT:
                player.move_x=-5
            if event.key==K_RIGHT:
                player.move_x=5
            if event.key==K_UP:
                player.move_y=-5
            if event.key==K_DOWN:
                player.move_y=5
            if event.key==K_z:
                sht=True
        if event.type==KEYUP:
            if event.key==K_LEFT:
                player.move_x=0
            if event.key==K_RIGHT:
                player.move_x=0
            if event.key==K_UP:
                player.move_y=0
            if event.key==K_DOWN:
                player.move_y=0
            if event.key==K_z:
                sht=False
    screen.fill((0,0,0))
#Orologio
    tp=clock.tick(40)
#Sparo Temporizzato
    Shoot()
#Aggiornamenti di schermo
    player.update()
    render_list.draw(screen)
    ch=PowTxt.render(str(charge),True,(255,255,255))
    screen.blit(ch,(10,10))
    for bullet in bullet_list:
        bullet.update()
    for powerup in pow_list:
        powerup.update()
    for enemy in enemy_list:
        enemy.update()
    pygame.display.flip()

Questo è quanto, saluti a tutti e buona programmazione!

Penaz.