El siguiente paso que he tomado, es gestionar la interacción con el mundo, es decir, los mensajes de dialogo entre NPC o con los objetos (como un cartel...)
Para esto, se me ocurrió utilizar una cola donde se agregaran, en pseudo codigo seria algo asi...:
cola_dialogo = []
mientras juego:
dibujar_mundo
dibujar_personajes
si no hay diálogos en la cola:
actualizar_mundo y personajes
si hay dialogo:
cola_dialogo[primer elemento]dibujar
cola_dialogo[primer elemento]actualizar
gestionar_eventos:
si se presiona tecla tal
retirar el primer elemento de la cola
Su implementación básica en Python y pygame es:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
import sys
from pygame.locals import *
ALTO = 480
ANCHO = 640
DEFAULTFONT = pygame.font.get_default_font()
class Dialogo():
def __init__(self, Dice="..."):
self.fuente = pygame.font.Font(DEFAULTFONT, 30)
self.dice = Dice
def imprime(self,pantalla):
titulo = self.fuente.render(self.dice, 1, (255,255,255))
pantalla.blit(titulo,(20,20))
def main():
pygame.init()
screen = pygame.display.set_mode((ANCHO,ALTO))
clock = pygame.time.Clock()
jugar = True
cola_dialogos = []
cola_dialogos.append(Dialogo("hola mundo"))
cola_dialogos.append(Dialogo(u"¿como estas?"))
cola_dialogos.append(Dialogo(u"Bien, y tú?"))
while jugar:
for event in pygame.event.get():
if event.type == pygame.QUIT:
jugar = False
if len(cola_dialogos)>0 and event.type == KEYDOWN:
if event.key == K_x:
cola_dialogos.pop(0)
clock.tick(25)
screen.fill((0,0,0))
if len(cola_dialogos)>0:
cola_dialogos[0].imprime(screen)
pygame.display.update()
main()
sys.exit()
consideremos que quiero la interacción con npcs, para eso, al "hablar" con un npc, lo que se aria, es agregar a la cola de eventos el dialogo correspondiente, en pseudo código:
cola_dialogo = []
mientras juego:
dibujar_mundo
dibujar_personajes
si no hay diálogos en la cola:
actualizar_mundo y personajes
si hay dialogo:
cola_dialogo[primer elemento]dibujar
cola_dialogo[primer elemento]actualizar
gestionar_eventos:
si se presiona tecla tal
retirar el primer elemento de la cola
si se presiona tecla tal y hay un personaje en rango:
agregar a cola dialogo de personaje
Lo bueno de utilizar una cola es que en caso de que dos personajes estén en rango, se agregan a la cola por quien este primero o mas cerca (aun que yo no he implementado eso ultimo):
Aquí dejo el código con dos NPCs, la imagen utilizada es la misma que del ejemplo de arriba. Aun me falta crear una versión del algoritmo del pintor para los sprites
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pygame.locals import *
import pygame
import sys
import os
from math import sin, cos, radians
from random import randrange
DEFAULTFONT = pygame.font.get_default_font()
ALTO = 640
ANCHO = 480
class Punto3D():
def __init__(self, x=0.0, y=0.0, z=0.0):
print x, ",", y, ",",z, "..."
self.x, self.y, self.z = x,y,z
def rotateX(self, angulo):
cosa = cos(radians(angulo))
sina = sin(radians(angulo))
y = self.y * cosa - self.z * sina
z = self.y * sina + self.z * cosa
self.y, self.z = y, z
def rotateY(self, angulo):
cosa = cos(radians(angulo))
sina = sin(radians(angulo))
z = self.z * cosa - self.x * sina
x = self.z * sina + self.x * cosa
self.x, self.z = x, z
def rotateZ(self, angulo):
cosa = cos(radians(angulo))
sina = sin(radians(angulo))
x = self.x * cosa - self.y * sina
y = self.x * sina + self.y * cosa
self.y, self.x = y, x
def a_2D(self, cdv=258, dis_obs=4, ven_anch=480, ven_alto=640):
#Cambia las cordenadas de 3D a 2D
#cdv = campo de visión
#dis_obs = distancia observador
try:
factor = cdv / (dis_obs + self.z)
except:
factor = cdv
#en la trasformación el 2 es para tomar el centro de la ventana
#como "origen"
x = int(self.x * factor + ven_alto / 2)
y = int(-self.y * factor + ven_anch / 2)
return (x, y)
class Cuadrado():
def __init__(self, dimenciones, centro, prof,
rellenar=False,
color=(255,255,255),
rejilla = True, tipo = "Horizontal", ancho=480,alto=640 ):
self.list_puntos = []
self.centro = centro
self.prof = prof
self.rejilla = rejilla
medio = dimenciones/2.0
x1 = centro[0]-medio
x2 = centro[0]+medio
y1 = centro[1]-medio
y2 = centro[1]+medio
self.ancho= ancho
self.alto = alto
puntos = [(x1,y1),(x1,y2),(x2,y1),(x2,y2)]
for p in puntos:
if tipo != "Horizontal":
self.list_puntos.append(Punto3D(p[0],p[1],prof))
else:
self.list_puntos.append(Punto3D(p[0],prof,p[1]))
self.aristas = [(0,1),(2,3),(2,0),(3,1)]
self.rellenar = rellenar
self.color = color
def dibuja(self,pantalla):
color = self.color
if self.rejilla:
for a in self.aristas:
#Esto dibuja las aristas....
pygame.draw.line(pantalla, color,
self.list_puntos[a[0]].a_2D(ven_anch = self.ancho, ven_alto = self.alto, cdv=256, dis_obs=4),
self.list_puntos[a[1]].a_2D(ven_anch = self.ancho, ven_alto = self.alto, cdv=256, dis_obs=4))
if self.rellenar:
lista_puntos = []
for i in [0,1,3,2]:
punto = self.list_puntos[i].a_2D(ven_anch = self.ancho, ven_alto = self.alto, cdv=256, dis_obs=4)
lista_puntos.append(punto)
pygame.draw.polygon(pantalla, color,lista_puntos)
class AnimacionDeArchivo():
def __init__(self, columnas, filas, image):
self.lista_frames = []
tile_w = image.get_width () / columnas
tile_h = image.get_height () / filas
for f in xrange (filas):
for c in xrange (columnas):
rect = c * tile_w , f * tile_h , tile_w, tile_h
self.lista_frames.append(image.subsurface(rect).copy ())
self._frame = 0
self.inicio = pygame.time.get_ticks()
self._delay = 1000/15
self.ultima_act = 0 #ultima
def redimencion(self, dimencion):
for i in range(len(self.lista_frames)):
frame_i = self.lista_frames[i]
frame = pygame.transform.scale(frame_i, dimencion)
self.lista_frames[i] = frame
def set_alpha(self, alpha):
for i in range(len(self.lista_frames)):
frame_i = self.lista_frames[i]
frame_i.set_alpha(alpha)
self.lista_frames[i] = frame_i
def Frame_es(self, Lista_anima = [0], Reset = False, Frame_return = False):
if Reset:
self._frame = 0
if pygame.time.get_ticks() - self.ultima_act > self._delay:
self._frame += 1
if self._frame >= len(Lista_anima):
self._frame = 0
self.ultima_act = pygame.time.get_ticks()
if Frame_return:
return self.lista_frames[ Lista_anima[self._frame] ], self._frame
else:
return self.lista_frames[ Lista_anima[self._frame] ]
class Sprite_3D(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
heroe = self.carga_imagen("heroe.png")
self.anima = AnimacionDeArchivo(3,3,heroe)
self.constante = 16
self.image = self.anima.Frame_es([7])
self.rect = self.image.get_rect()
self.rect.center = (ANCHO/2,ALTO/2)
self.punto_alto = Punto3D(0,0.5,0)
self.punto_base = Punto3D(0,0,0)
self.const = self.punto_base.a_2D()[1] - self.punto_alto.a_2D()[1]
self.estado = "parado"
self.fuerza_salto = 0.1
self.gravedad = 0.01
self.temp = (0,0)
def carga_imagen(self, name): #función para cargar las imagenes
fullname = os.path.join(name)
try:
image = pygame.image.load(fullname)
except pygame.error, message:
print 'No se puede cargar la imagen: ', fullname
raise SystemExit, message
image = image.convert_alpha()
return (image)
def cambiar_estado(self, nuevo_estado):
self.anima.Frame_es(Reset=True)
self.estado = nuevo_estado
def parado(self):
key=pygame.key.get_pressed()
self.image = self.anima.Frame_es([7])
if key[K_x]:
self.cambiar_estado("salta")
self.temp = (self.punto_base.y, self.punto_alto.y)
if key[K_UP]:
self.cambiar_estado("fondo")
if key[K_DOWN]:
self.cambiar_estado("frente")
if key[K_LEFT]:
self.cambiar_estado("izquierda")
if key[K_RIGHT]:
self.cambiar_estado("derecha")
def al_fondo(self):
key=pygame.key.get_pressed()
if key[K_UP]:
self.image = self.anima.Frame_es([0,0,1,1,2,2,1,1])
self.punto_base.z += 0.1
self.punto_alto.z += 0.1
else:
self.cambiar_estado("parado")
def al_frente(self):
key=pygame.key.get_pressed()
if key[K_DOWN]:
self.image = self.anima.Frame_es([6,6,7,7,8,8,7,7])
if (self.punto_base.z-0.1) > -4:
self.punto_base.z -= 0.1
self.punto_alto.z -= 0.1
else:
self.cambiar_estado("parado")
def derecha(self):
key=pygame.key.get_pressed()
if key[K_RIGHT]:
self.image = self.anima.Frame_es([3,3,4,4,5,5,4,4])
self.punto_base.x += 0.1
self.punto_alto.x += 0.1
else:
self.cambiar_estado("parado")
def izquierda(self):
key=pygame.key.get_pressed()
if key[K_LEFT]:
image = self.anima.Frame_es([3,3,4,4,5,5,4,4])
self.image = pygame.transform.flip(image, True, False)
self.punto_base.x -= 0.1
self.punto_alto.x -= 0.1
else:
self.cambiar_estado("parado")
def salta(self):
if (self.punto_base.y + self.fuerza_salto) < self.temp[0]:
self.fuerza_salto = 0.1
self.cambiar_estado("parado")
self.punto_base.y = self.temp[0]
self.punto_alto.y = self.temp[1]
else:
self.punto_base.y += self.fuerza_salto
self.punto_alto.y += self.fuerza_salto
self.fuerza_salto -= self.gravedad
def update(self):
if self.estado == "frente":
self.al_frente()
elif self.estado == "fondo":
self.al_fondo()
elif self.estado == "derecha":
self.derecha()
elif self.estado == "izquierda":
self.izquierda()
elif self.estado == "salta":
self.salta()
else:
self.parado()
self.const = self.punto_base.a_2D()[1] - self.punto_alto.a_2D()[1]
self.image = pygame.transform.scale(self.image, (self.const,self.const))
self.rect = self.image.get_rect()
self.rect.center = self.punto_base.a_2D()
class Dialogo():
def __init__(self, Dice="..."):
self.fuente = pygame.font.Font(DEFAULTFONT, 30)
self.dice = Dice
def imprime(self,pantalla):
titulo = self.fuente.render(self.dice, 1, (255,255,255))
pantalla.blit(titulo,(20,20))
class Sprite_3D_NPC(pygame.sprite.Sprite):
def __init__(self, x,y):
pygame.sprite.Sprite.__init__(self)
heroe = self.carga_imagen("heroe.png")
self.anima = AnimacionDeArchivo(3,3,heroe)
self.constante = 16
self.image = self.anima.Frame_es([7])
self.rect = self.image.get_rect()
self.rect.center = (ANCHO/2,ALTO/2)
self.punto_alto = Punto3D(x,0.5,y)
self.punto_base = Punto3D(x,0,y)
self.const = self.punto_base.a_2D()[1] - self.punto_alto.a_2D()[1]
self.estado = "parado"
self.fuerza_salto = 0.1
self.gravedad = 0.01
self.temp = (0,0)
self.dialogo = Dialogo(u"Hola, ¿De donde vienes?")
def carga_imagen(self, name): #función para cargar las imagenes
fullname = os.path.join(name)
try:
image = pygame.image.load(fullname)
except pygame.error, message:
print 'No se puede cargar la imagen: ', fullname
raise SystemExit, message
image = image.convert_alpha()
return (image)
def update(self):
self.const = self.punto_base.a_2D()[1] - self.punto_alto.a_2D()[1]
self.image = pygame.transform.scale(self.image, (self.const,self.const))
self.rect = self.image.get_rect()
self.rect.center = self.punto_base.a_2D()
class Campo():
def __init__(self, punto, dimenciones, prof = 0):
self.punto = punto
self.prof = prof
self.dimenciones = dimenciones
self.campo = []
mapa = ["zzzzz zxzzzx",
" zzzzxzzx ",
"zy x z y zz",
" zzxz zzz zz",
" zxz z ",
"zxzz yzz y"]
self.genera(mapa)
def genera(self,mapa):
y = 0
for fila in mapa:
x=0
for columna in fila:
if columna != " ":
centro = ((self.punto[0]+(x)), (self.punto[1]-(y)))
print "el centro es: ", centro
cuadrado = Cuadrado(self.dimenciones,centro,self.prof )
if columna == "x":
cuadrado.rellenar = True
cuadrado.rejilla = False
if columna == "z":
cuadrado.color = (20,20,250)
if columna == "y":
cuadrado.color = (100,100,255)
cuadrado.rellenar = True
self.campo.append(cuadrado)
x+=self.dimenciones
y+=self.dimenciones
def dibuja(self, pantalla):
for cuadrado in self.campo:
cuadrado.dibuja(pantalla)
def main():
pygame.init()
screen = pygame.display.set_mode((ALTO, ANCHO))
clock = pygame.time.Clock()
cuadrado = Campo((-6,7),6,-1)
jugar = True
grupo = pygame.sprite.RenderClear()
npcs_g = pygame.sprite.RenderClear()
spri = Sprite_3D()
spri_2 = Sprite_3D_NPC(-2,0)
spri_3 = Sprite_3D_NPC(1,-2)
grupo.add(spri)
npcs_g.add(spri_2)
npcs_g.add(spri_3)
cola_dialogos = []
while jugar:
for event in pygame.event.get():
if event.type == pygame.QUIT:
jugar = False
if event.type == KEYDOWN:
if event.key == K_z:
if len(cola_dialogos)>0:
cola_dialogos.pop(0)
else:
for sprite in npcs_g:
if (sprite.punto_base.x < spri.punto_base.x+0.5 and sprite.punto_base.x > spri.punto_base.x-0.5) and (sprite.punto_base.z < spri.punto_base.z+0.5 and sprite.punto_base.z > spri.punto_base.z-0.5):
cola_dialogos.append(sprite.dialogo)
clock.tick(25)
screen.fill((200,200,200))
cuadrado.dibuja(screen)
grupo.draw(screen)
npcs_g.draw(screen)
if len(cola_dialogos)>0:
cola_dialogos[0].imprime(screen)
else:
grupo.update()
npcs_g.update()
pygame.display.update()
main()
sys.exit()
¿Qué les parece?
