Razón Artificial

La ciencia y el arte de crear videojuegos

Engine XI: Creando al héroe

Escrito por adrigm el 14 de junio de 2010 en Desarrollo Videojuegos, Noticias, Programación | 40 Comentarios.

Ya tenemos listo nuestro mapa, preparado para ser puesto en pantalla, pero en un juego RPG la cámara sigue al héroe, es decir, se ve el trozo de mapa alrededor del héroe, por eso necesitamos antes de representar el mapa a nuestro héroe para poder representar la parte que necesitamos del mapa.

Cortando un chareset

Un charaset es una imagen que contiene todas las posiciones de un personaje, que superpuestas crean una animación, como vale más una imagen que mil palabras dejo un ejemplo.

Como vemos, este simula el movimiento de caminar en 4 direcciones, se podría hacer uno de 8 direcciones, pero el nuestro caminará tan solo en cuatro.

Vale lo que tenemos que hacer es parecido a cuando cortamos el tileset, cortat cada uno de los frames de nuestro charaset y almacenarlos en una array, esta vez será bidimensional, así que con solo ir cambiando los índices del array podemos mostrar un frame u otro.

Creamos una funcion en nuestro archivo funciones.py debajo de la función de cortar tilesets.

# Corta un chara en las fil y col indicadas. Array Bidimensional.
def cortar_chara(ruta, fil, col):
	image = load_image(ruta, True)
	rect = image.get_rect()
	w = rect.w / col
	h = rect.h / fil
	sprite = range(fil)
	for i in range(fil):
		sprite[i] = range(col)
		
	for f in range(fil):
		for c in range(col):
			sprite[f]1 = image.subsurface((rect.left, rect.top, w, h))
			rect.left += w
		rect.top += h
		rect.left = 0
		
	return sprite

Como vemos muy parecida a la de cortar tilesets, pero esta vez a parte de almacenarlo en un array bidimensional, también le decimos cuantas filas y columnas tiene nuestro charaset. Esto lo hacemos así para que sea mas universal y podamos almacenar todo tipo de charas de cualquier tamaño, solo debemos especificas cuantas filas y columnas y el se encargará de cortarlo.

La clase Actors

Vamos a crear el fichero que se encargará de gestionar a nuestro héroe, ya sabes hacemos una copia del archivo plantilla.py y creamos el archivo actors.py. y creamos la clase Actors.

class Actor:
	def __init__(self, pos):
		self.pos = pos
		self.chara = cortar_chara("graphics/charasets/chara.png", 4, 4)
		
		self.image = self.chara[0][0]
		self.rect = self.image.get_rect()
		
	def dibujar(self, screen):
		screen.blit(self.image, self.rect)

De momento para el método de inicialización recibe la posición del charaset. en un futuro debería recibir otra como la imagen y demás, pero ya habrá tiempo de generalizar el código cuando este la base.

Almacenamos la posición inicial en self.pos y creamos un array bidimensional llamado self.chara con la función que acabamos de crear. En nuestro caso tiene 4 filas y 4 columnas.

Luego definimos que frame está activo en self.image en este caso y a modo de prueba cogemos el [0, 0] es decir el de la primera fila y la primera columna de nuestro chara. A continuación obtenemos el rect de esa imagen.

Por último hacemos a modo de esbozo el método dibujar que será el encargado de gestionar el dibujado de nuestro chara. Por ahora es muy simple y solo muestra en pantalla el chara. Atención a que recibe como parámetro la variable screen, esto es nuestra pantalla, porque debe saber donde tiene que dibujarlo.

Dibujando en pantalla

Hasta ahora código, teoría y mas código, alguna salida en consola, pero nuestra ventana sigue en negro. Bueno para los desesperados vamos a mostrar nuestro chara en pantalla para ver que realmente funciona lo que estamos haciendo.

nos vamos a nuestro engine.py, el archivo principal y le añadimos lo siguiente.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Módulos
import sys, pygame
from pygame.locals import *

from maps import *
from actors import *

# Constantes
WIDTH = 640
HEIGHT = 480

# Clases
# ---------------------------------------------------------------------

# ---------------------------------------------------------------------

# Funciones
# ---------------------------------------------------------------------

def salir():
	keys = pygame.key.get_pressed()
	for eventos in pygame.event.get():
		if eventos.type == QUIT:
			sys.exit(0)
		if keys[K_ESCAPE]:
			sys.exit(0)

# ---------------------------------------------------------------------

def main():
	screen = pygame.display.set_mode((WIDTH, HEIGHT))
	pygame.display.set_caption("Engine RPG")

	clock = pygame.time.Clock()
	
	mapa = Mapa("bosque.tmx")
	heroe = Actor(mapa.start)

	while True:
		time = clock.tick(60)
		salir()
		
		heroe.dibujar(screen)
		pygame.display.flip()
	return 0

if __name__ == '__main__':
	pygame.init()
	main()

Primero importamos nuestro módulo actors después del módulo maps. Creamos un objeto Actor llamado heroe debajo del objeto Mapa. Atención a que tiene que ser despues y no antes porque recibe como parámentro mapa.start como la posición inicial y esta esta dentro de la clase Mapa. por último tenemos que llamar a su método dibujar dentro del bucle principal con heroe.dibujar(screen).

Debería mostrarse el primer frame de nuestro chara en la parte superior izquierda de la pantalla.

40 Comentarios en "Engine XI: Creando al héroe"

  1. Bline dice:

    Lo que realmente me gusta de la idea del proyecto es en si la flexibilidad que se le puede dar a la hora de programarlo. A diferencia del rpgmaker que no te deja salvo lo estricto aqui puedes hacer, igual con un poco bastante más de esfuerzo, algo muchísimo mejor.

  2. admin dice:

    De eso se trata programar a pelo y no hacerlo con un programa. Hombre todo tiene sus pros y sus contras, programar un juego desde 0 es mucho mas trabajoso, hay que hacerlo todo como hemos visto.

    A nivel amateur como es nuestro caso, yo que me siento programador pues prefiero hacerlo así porque lo que me gusta es resolver problemas y ver como puedo hacer las cosas por mí mismo. No espero que este proyecto compita con el maker, pero si divertirme haciéndolo y aprender como se hacen videojuegos desde 0. Y si de paso al poner mi experiencia aquí a alguien le sirve pues mejor que mejor.

    Gracias por seguir los artículos, con que haya alguien interesado en él ya es un motivo para exponer aquí los avances, ya estoy trabajando en nuevas entregas.

  3. Bline dice:

    ¿Hasta donde piensas llegar con el tutorial admin? Lo digo porque esto puede prometer :p

  4. devil dice:

    Hola admi, quería decirte que es un buen tutorial, yo lo sigo desde el principio y la verdad es que es muy bueno .
    Un saludo, y espero que sigas con los tutoriales

  5. admin dice:

    Bline, hasta donde se pueda. Mi objetivo es desarrollar un Engine completo, pero eso serán muchas y muchas entregas. Vamos poco a poco, pero por ahora te puedo decir que no tengo en mente dejarlo y que ya hay muchos mas avances hecho que necesito crearles un tutorial.

    Un saludo.

  6. Ubuntero dice:

    Ánimo con los tutoriales, son interesantes y no es facil a veces encontrar tutoriales extensos, fracionados, fáciles de seguir y ambiciosos :-)

  7. admin dice:

    Ubuntero, no lo es, y yo lo hago a ver si se toman ejemplos y la gente se anima a hacer cosas parecidas. Que cuando quieres aprender algo siempre es ir buscándose uno la vida para todo.

  8. admin dice:

    Pronto Bline, he estado liado y los tutoriales que vienen a continuación tienen chicha. Pronto habrá novedades jugosas.

  9. Bline dice:

    xD lo decía en broma hombre! :p que se que escribir y sobretodo hacerlo comprensible es muy difícil y mas si tienen “chicha” como yo xD

  10. Roberto dice:

    Quisiera felicitarte ADMIN estos tutoriales no los encuentras en ningún sitio tan bien explicados como lo están aquí.
    Estamos deseosos de que salga el número XII por que esto va enganchándome cada vez más.
    Un saludo y sige así

  11. Anker dice:

    Muy buenos tutos me ha gustado mucho, gracias y estoy a la espera de los proximos un saludo y sigue asi

  12. Emlett dice:

    Como todos los comentarios anteriores, te felicito por los tutoriales.
    Me encontre con tu pagina de casualidad y me han ayudado mucho.
    Mi juego quiero hacerlo como metal slug, pero tu tutorial, aunque sea para rpg, me a ayudado mucho en el avance de este.

    Muchas gracias y sigue asi, tienes todo nuestro apollo!!!! =)

  13. admin dice:

    Gracias a todos por el apoyo, así dan ganas de seguir, estoy trabajando en nuevas entragas, que eso nadie lo dude.

    Emlett, puedes usar el foro para exponer tu proyecto así entre todos podemos ayudarte y demás.

  14. David dice:

    Siempre he querido hacer un jueguecito de estos (sin el RPGmaker, que eso no vale jaja).

    De Python no tengo ni idea, pero lo mismo un día me pongo y lo hago en Java que es lo mio.

    En todo caso ánimo, te está quedando un tutorial muy en condiciones ^^.

  15. TeCh dice:

    Comentario desde un usuario avanzado que se ha pegado de ostias en el mercado laboral como un programador que le encanta su trabajo pero no se ve recompensado por su trabajo: “Eres la polla!”. Te digo esto porque pienso que en este pais estamos sobrados de mentes maravillosas como la tuya, que no se queda en estrujarse para llegar a saber mas, sino que siente la necesidad de compartirlo para llegar a un bien bastante mejor!

    Es uno de los tutoriales que mas me han gustado, aunque no tenga intencion de hacer un videojuego, solamente leyendo el tutorial(que por ahora me lo he pimplao enterito!) entran ganas de hacer uno! xD

    Me alegro muchsimo que haya gente que quieran superarse y hacer que la gente aprenda de lo aprendido y te animo a que sigas indagando sobre el tema.

    A mi me anda rondando la idea de montar un sistema base sobre php para creacion de paginas web con gestor de contenido, en lo que todo sea automatizado y a la vez gestionado por el propio usuario (con los quebraderos de cabeza que eso conlleva… xD).. algun dia de estos caera la breba pero hasta entonces, me encantara leer articulos como el tuyo!

    El conocimiento no ocupa lugar, y en nuestra profesion… es nuestro principal arma!!!!

    Un abrazo! :D

  16. adrigm dice:

    TeCh, muchas gracias por tu comentario, son de los que motivan a seguir. Si todos fuéramos capaces de intentar dejar constancia de nuestro trabajo y como hemos conseguido las cosas el conocimiento que habría podría ser brutal, por eso yo aporto lo que buenamente puedo para todos.

    Te animo con tu proyecto y todo es cuestión de ganas y paciencia, yo hace un año no sabía mas que hacer programas en la consola y ahora mira.

    Un saludo.

  17. TeKNo dUKe dice:

    Buen tutorial. La verdad que dan ganas de ponerse a programar el engine. Pregunta pensaste en algun momento poner un svn o algo similar para poder bajarnos la ultima versión del enige tal cual la tienes tu? Igual algun .tag.gz con el proyecto y agregarlo a las entradas.

    Quedo pendiente a nuevas entregas, gracias por compartirlo con todos :).-

  18. V@CHY dice:

    Muy bueno che!!! Parece que esperamos el XII, vamos a probar como es esto… consulta: en el chareset la columna 1 y 3 son iguales, esto es intencional o se puede obviar una de estas???

    Abrazo y muchas gracias por la info, se valora mucho!

  19. trigop dice:

    Muy buen trabajo compañero! Felicidades por el trabajo que estas haciendo y sobre todo por facilitar a las personas que empiecen una buena base para enterarse de que va todo esto. Mucha suerte y energía y esperamos leerte a menudo!

    Un abrazo.

  20. Zombie dice:

    Excelente… llegué acá por el gmail que anunció una noticia del barrapunto.com

    Sigue así, que se necesita y se agradece la experiencia de otros.

    Saludos.

    ó.ò

  21. Olimpiodoro dice:

    Sé que está fuera de contexto pero ¿qué realiza exactamente la función append? Me perdí varias clases de python y hay algunas cosas que no entiendo del todo por los cabos sueltos, así como tampoco conozco con total certeza range.

  22. adrigm dice:

    Olimpiodoro, deberías mirar Python bien antes de seguir.

    append es un método que tienen todas las listas para añadirle un elemento al final.

    Por ejemplo:

    lista = [4, 6, 8]
    lista.append(5)
    

    eso hace que nuestra lista ahora sea:

    [4, 6, 8, 5]

  23. unomas dice:

    Muchas gracias por el tuto, podrías dejar una fecha aproximada de publicación de la siguiente entrega, la cual podrías modificar en función de tus retrasos/adelantos, es para no mirar el tuto todas las semanas (engancha).

    Olimpiodoro, abrir cualquier cosa como append siempre es para añadir algo al final, es decir, que el puntero de escritura se pone al final tras el último elemento, eso para todos los lenguajes en sus funciones de apertura de ficheros (aquí listas pero es el mismo principio). Pues los modos de apertura write (escribir) o read (leer) en una función open() abren el fichero y situan el puntero al principio.

  24. adrigm dice:

    unomas, el proyecto está llevando a cabo una restructuración total, reescribendose desde el principio, hay ya más hecho de lo que hay en los tutos, pero habrá que hacer una serie nueva, me temo.

  25. unomas dice:

    Vaya, acabo de ir al origen del blog y veo que estás escribiendo acerca de tus nuevos avances: reestructuración, reedición, …

    Es lo que pasa por venir de barrapunto y directamente al listado de artículos del engine.

    Gracias por tu tiempo y dedicación.

  26. Wadk dice:

    Creo que la función cortar_chara quedaría mejor así:

    def cortar_chara(ruta,col=4,fil=0):
    	if fil==0:
    		fil=col;
    	image=load_image(ruta,True);
    	rect=image.get_rect();
    	w=rect.w/col;
    	h=rect.h/fil;
    	sprite=range(fil);
    	for i in range(fil):
    		sprite[i]=range(col);
    
    	for f in range(fil):
    		for c in range(col):
    			sprite[f]1=image.subsurface((rect.left,rect.top,w,h));
    			rect.left+=w;
    		rect.top+=h;
    		rect.left=0;

    porque supongo que generalmente los charasets son de 4*4, y así no hay que especificarlo si es el caso. Al mismo tiempo, si se quiere poner un charaset de otro tamaño, es probable que el número de filas y columnas sea el mismo, y así sólo hay que especificar el número de columnas y el número de filas se obtiene automáticamente.

    Además, el número de columnas debería ir antes, porque por convención lo que tiene que ver con ancho va antes de lo que tiene que ver con alto.

  27. adrigm dice:

    Wadk, pero tosas las suposiciones que haces la vuelven menos genérica, yo prefiero dejar que el usuario elija el tamaño del chara (en realidad lo más común es 3×3 y no 4×4) y que pueda especificar el numero de filas y columnas, no todas los sprites tienen animaciones en ambos sentidos.

    Precisamente como lo que se mide ante es el ancho por convención primero deben ir las filas, se toma una fila y se recorren sus columnas, se toma la siguiente file y se recorren sus columnas. Yo al menos trabajo así.

  28. Wadk dice:

    Pero igual se puede especificar. Parece que no sabés sobre valores por defecto en argumentos de funciones, lo cual me impresionaría si es cierto.
    Usando la función como la puse antes, este código sería correcto:

    cortar_chara("a.png"); # Sprite de 4*4
    cortar_chara("b.png",5); # Sprite de 5*5
    cortar_chara("c.png",3,2); # Sprite de 3*2

    Lo de las columnas y filas, supongo que depende de por dónde se mire.

  29. Wadk dice:

    Por cierto, noto que se me olvidó el “return sprite;” en mi otro post :P

  30. adrigm dice:

    Wadk, claro que se de argumentos por defecto, pero pienso que para este caso no es necesario y tiende a confundir o a causar errores difíciles de detectar, prefiero dejar los argumentos por defecto para cuando realmente a falta de valor halla que usar uno concreto.

    No me parece el caso de cortar los sprites que puede haber en cada juego de diferentes grados de animación. Porque no pienses solo en el caminar, piensa en uno de saltar tendría varios frames más, yo la verdad prefiero dejarlo si parámetros por defecto.

  31. Wadk dice:

    Bueno, a mi me parece una buena idea. Pero es cuestión de preferencia personal, supongo.

  32. tony dice:

    una pregunta no sabes como puedo hacer los diálogos ,para simular una conversación entre personajes ,me ayudarías mucho.
    gracias.

  33. Pedro dice:

    Excelente trabajo, me ha servido muchísimo.

    Saludos!

  34. mirko dice:

    Porque !!! justo ahora que estaba programando TwT se acabo el tutorial .__. no me aparecio nada en algo me equivoque

  35. Ludivia dice:

    De acuerdo con Mirko, es una lástima que en este blog haya tantas cosas incompletas, que de haber seguido se hubieran llegado a cosas realmente grandiosas.
    Simplemente se deja un proyecto y se comienza otro sin más.

  36. JunKa Ortega dice:

    No puedo avanzar me sale el siguiente error:

    sprite[i] = range(col)

    TypeError: ‘range’ object does not support item assignment

  37. Braian dice:

    No hay alguna posibilidad de que continues con los tutoriales del engine? Muchisimas gracias!! Saludos.

  38. Juan Soza dice:

    amigo buen dia cuando corro el sistema me sale un error que es el sigueinte no se si me podrias ayudar gracias
    ubuntu@ubuntu:~/Desktop/engine$ python engine.py
    Traceback (most recent call last):
    File “engine.py”, line 52, in
    main()
    File “engine.py”, line 40, in main
    heroe = Actor(mapa.start)
    File “/home/ubuntu/Desktop/engine/actors.py”, line 14, in __init__
    self.rect = self.image.self.get_rect()
    AttributeError: ‘tuple’ object has no attribute ‘self’
    ubuntu@ubuntu:~/Desktop/engine$

  39. ariel dice:

    me sale el mismo error que juan soza, pero es ‘int’ object has no attribute ‘get_rect’

Deja un comentario