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.