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? 
