Undercode

(GameDev + Programming + Randomness) * Caffeine²
  • rss
  • Home
  • About
  • Games
  • Tutoriais

Oblivious —the darkness within—

Quildreen | December 14, 2009

After deciding that I was going to enter the Ludum Dare #16, despiste the almost completly lack of time I`d have to work on the weekend, I was able to finish my entry, somehow. Of course, it didn`t turned out like I wanted it to be, most because of the lack of time, but also because I hadn`t really a definite set goal until the last hours.

Deciding to go with javascript was a lifesaver. At first I wanted to write my game with Python and PyGame, but I`d end up spending too much time on rendering and UI implementation, and with javascript that was already hard-coded into the browser for me, so all that`d be left was to write the game logic. Of course, javascript has its issues, like you`re not guaranted fully compatibility between browsers (even with the standard ones, something may behave oddly), and that`s what made me cut that M$ IEerie from the list of supported browser before I even started writing a line of code.

—the concept—

You’re trapped within the labyrinth of consciousness, blocked out by the silly wishes of peace of mind. You’re the Truth, fighting with all you’ve got to bring some light into a world that decided to go on living the “Ignorance is a bliss” saying.

But what to do? Is there anyone who wants to know the truth when you can live “better” with a sweet lie? Maybe, or maybe you have to force your way out, bringing those painful memories that are hidden in the deepest of a broken heart to daylight.

Is it cruel? Maybe, but not more than knowingly turning your face away from what you have done. What lies beyond those fragments of memory, lingering in the dark maze of consciousness? More importantly, will you have the courage to face the unknown or will you choose the large path, living on, in eternal oblivion…?

As you see, I think with stories. At first, I was going for a Turn-Based Strategy game with some dungeon crawling, however, since I couldn’t spend too much time working on a fully fledged procedural dungeon building algorithm, I went with the easier maze generation. Yeah, I could’ve just ported the algorithm I’ve written for Underground Tower, but it’s not really done, and there’s lots of issues there I hadn’t the time to address yet.

The player objective would be to collect memories and uncover the game’s story (which I still haven’t thought of besides that introduction up there). While exploring the maze of consciousness the player would gradually fade out by the sweet lies that lies at each one of the walls of the labyrinth, eating away any “silly” try to look at the “real” world. Therefore, collectable reasonings for staying alive would be found lying around the place.

The memories where when the story of the game would come, but I haven’t had the time to code this part. Not that it would be difficult to code the part of collecting memories (it was just a matter of inheriting the cCollectable class), but to arrange it to make a good story would.

It all could’ve work… if only I haven’t tomorrow’s trip planned.


Concept art for Lucy, the bestowal of light (?)

—the goodies—

That said, there were some good parts about this game. First, the wonders of javascript prototyping. I was afraid to don’t even finish my entry, but I was able to accomplish a fair amount of work without really having a gaming library (mine isn’t complete and it hasn’t much useful things for games right now). Kudos for the Prototype and Scriptaculous libraries for making my life easier too, working with pure javascript is not that fun, must I say, but these libraries make your life a heck easier.

I’ve also learned to write a procedural maze generator using Depth-First Search algorithm. It was quite fun, easy and quick to implement, actually, and it works neat. I think I couldn’t ask for more, could I?

Surprisingly, I had time to sketch a main menu background and a javascript motivational. I wanted to color the motivational, but I’m dog slow at this and don’t have a tablet. So… yeah, it’d take me ages D:

Last, but not least, I had quite fun participating. Even with all the swearing while I was shopping and looking at the clock, seeing I was wasting a precious, precious time. Well, I wish I spent more time chatting with peoples on IRC and so, I haven’t really talked on IRC, I guess.

—the baddies—

Well, now to the part that really matters: what went wrong.

  • Lack of time (and this must come first, as I planned a game without the proper time to implement it)
  • Light handling on the maze has a bug which won’t fade the illuminated cells if they can’t be reached by the player’s light radius.
  • Didn’t had the time to implement things I wanted, like the story and the auto-explorers (whom could be cast to search for something)
  • Points are meaningless when they don’t mean anything in game.
  • No sounds and music whatsoever. I’d want to put a suspenseful music together with the story, and maybe some footsteps sound. But haven’t time for neither.
  • Lack of gameplay and difficult progression, since I haven’t time to test any other than “Does it works? Fine, submission where?”
  • Things takes so long to load, and there’s no preloader for images. Minifying the javascript and putting everything on a single source file would solve this, though.

—the conclusion—

While I have other projects to work with (RED -spider lily- and some libraries), I think I’ll work on this game first. Either because it’s simpler and shouldn’t take me too long to finish, and because working on it I can work on my Lily library at the same time. And I’m thinking about porting RED -spider lily- to web, instead of continue on Python. But that will depend on some design issues I still have to address.

Some feedback on the game would be really nice :3

Comments
No Comments »
Categories
Casual, Contests, JavaScript
Tags
Game Development, ludum dare, oblivious —the darkness within—, postmortem
Comments rss Comments rss
Trackback Trackback

Bookmark this article! [?]

Del.icio.usDiggDiigoFacebookFurlGoogleWindows LiveRedditTechnoratiYahoo

O Estranho Mundo das Partículas

Quildreen | November 24, 2009

Efeito construídos com partículas

Elas são as estrelas dos jogos 3d… literalmente. Mas também compõe chamas, fumaça, brilho, chuva, neve, e outra infinidade de efeitos e fenômenos naturais que seriam mais difíceis de se implementar usando técnicas convencionais do que conseguir 100% em jogos da Atlus. Os sistemas de partículas existem justamente para isso: reproduzir esses fenômenos caóticos em jogos; e embora eles sejam mais freqüentemente usados em jogos 3d, não significa que o 2d não possa se beneficiar de suas técnicas — aliás, elas estão se tornando cada vez mais um hit entre jogos 2d casuais. E se você chegou até aqui, é provável que em algum momento tenha se perguntado:

Como eu uso isso no meu jogo?

O objetivo deste tutorial é tentar responder à esta pergunta, e para isso, além de uma boa dose de teoria sobre como os sistemas de partículas funcionam e considerações sobre as técnicas a serem usadas — afinal, estaremos atualizando e renderizando centenas (ou até milhares) objetos na tela ao mesmo tempo, performance é um ponto crítico aqui, — vamos construir um sistema completo e flexível em Python e PyGame. Portanto, prepare as bibliotecas, pegue seu editor de código preferido (Eu escolho você, SciTe!) e vamos capturar o maior número de pokémons… er, digo, construir um sistema de partículas.

- O que são partículas? -

Um sistema de partículas é simplesmente uma coleção de objetos no espaço. Esses objetos são dinâmicos, e passam por todo um ciclo de vida. As partículas nascem, crescem e, finalmente, morrem. Em nosso mundo, poderíamos considerar a chuva como um sistema de partículas complexo, com cada uma das gotas de chuva sendo uma partícula. Essas gotas “nascem” nas nuvens, são atraídas para o centro da terra pela força da gravidade e, ao colidir com algum objeto, “morrem”. Sua “morte”, no entanto, resulta em um novo sistema de partículas: diversas gotículas “nascem” no ponto onde a gota de chuva colidiu, tomam direções e velocidades aleatórias e, ao colidir com algum objeto, “morrem”, dando origem à mais um sistema de partículas. Esse poderia ser um outro sistema com novas gotículas, ou um sistema que cria uma poça d’água. As possibilidades são infinitas, já que no universo essas partículas sempre se tornarão algo novo.

Em jogos, no entanto, chegará uma hora em que as partículas têm realmente que morrer. Por exemplo, uma vez que a fumaça se dissipou completamente, você não precisa criar um sistema de partículas que simule a atomsfera, porque essa não seria visível. Ainda há outra limitação, que é a capacidade de processamento dos computadores. Isso limita o número de partículas que você pode controlar e renderizar ao mesmo tempo em uma cena, sem afetar muito a performance. Note que eu disse “sem afetear muito”, lembre-se que as partículas sempre diminuirão razoávelmente a velocidade (Frames Per Second) do jogo. É por isso que, em um FPS, depois de um certo tempo, algumas paredes que possuíam buracos de bala feitos por você voltam milagrosamente ao normal.

- Como elas funcionam? -

Um sistema de partículas é composto por um emissor, que é o responsável por criar as partículas, e as partículas propriamente ditas. Voltando ao caso da chuva, a nuvem seria o emissor e cada uma das gotas de chuva uma partícula. A cada frame o emissor atualiza as partículas que ele controla, além de decidir se deve ou não criar novas partículas. Ele também é responsável por destruir as partículas mortas.

Então, recapitulando o papel do emissor:

  • Manter uma lista das partículas ativas controladas pelo emissor.
  • Atualizar as partículas ativas na tela.
  • Verificar se alguma das partículas morreu e retirá-las da lista de partículas.
  • Criar novas partículas, se necessário.

Com isso, podemos criar uma pequena base para nosso objeto, um cAbstractEmitter (ou classe EmissorAbstrato, mas inglês é a linguagem universal em programação, não é?). Lembrando que por ser um objeto abstrato, ele precisa ser bem flexível e extensível para outros tipos de emissores.

  1. import random
  2. class cAbstractEmitter:
  3.     def __init__(self, *args, **kwargs):
  4.         “”"
  5.         Inicializa o emissor de partículas.
  6.         ”"”
  7.         
  8.         ## Um id informando o tipo deste emissor. Assim podemos
  9.         ## agrupar emissores que usam uma mesma textura juntos
  10.         ## e renderizá-los mais rápido.
  11.         ## …mas isso não é tão prático no caso da PyGame pura.
  12.         self.id = kwargs.pop(“id”, “abstract”)
  13.         
  14.         ## Uma lista das partículas controladas por este emissor.
  15.         self.particles = []
  16.         
  17.         ## Uma lista das partículas mortas. Como alocar e desalocar
  18.         ## memória a cada frame é pesado, nós mantemos as partículas
  19.         ## mortas na memória e reinicializamos elas quando for preciso.
  20.         self.dead_particles = []
  21.         
  22.         ## A posição que este emissor se encontra na tela.
  23.         self.x = kwargs.pop(“x”, 0)
  24.         self.y = kwargs.pop(“y”, 0)
  25.         
  26.         ## O número de partículas ativas controladas por este emissor.
  27.         self.active_particles = 0
  28.         
  29.         ## O número máximo de partículas que podem ser criadas por frame.
  30.         self.emits_per_frame = kwargs.pop(“emits_per_frame”, 1)
  31.         
  32.         ## Uma variação do número máximo de partículas criadas por frame.
  33.         ## Pode ser positivo ou negativo, e será somado ao número máximo
  34.         ## de partículas por frame para calcular quantas devem ser criadas.
  35.         self.emits_variant = kwargs.pop(“emits_variant”, 0)
  36.         
  37.         ## O número máximo de partículas que este emissor pode controlar.
  38.         self.max_particles = kwargs.pop(“max_particles”, 100)
  39.         
  40.        
  41.     def emit_particles(self, num_particles):
  42.         “”"
  43.         Emite novas partículas.
  44.         ”"”
  45.         pass
  46.                 
  47.     def update(self, lag):
  48.         “”"
  49.         Atualiza todas as partículas ativas controladas por este emissor.
  50.         Verifica se existem partículas mortas, e as move para a lista de
  51.         partículas mortas. Depois disso, cria novas partículas, se for
  52.         necessário.
  53.         ”"”
  54.         
  55.         ## Se for possível, cria novas partículas. A variação no número de
  56.         ## partículas é calculada multiplicando-se a base de variação por
  57.         ## um número aleatório entre -1 e 1.
  58.         new_particles = self.emits_per_frame + int(self.emits_variant * random.uniform(-1, 1))
  59.         self.emit_particles(new_particles)
  60.         
  61.         ## Uma lista das partículas que morreram neste frame
  62.         dead_particles = []
  63.         
  64.         ## Uma lista de pontos da tela que precisamos atualizar
  65.         dirty_rects = []
  66.         
  67.         ## Atualiza as partículas ativas. Cada partícula tem uma função
  68.         ## de atualização que retorna se elas estão vivas ou não.
  69.         for particle in self.particles:
  70.             alive, dirty = particle.update(lag)
  71.             dirty_rects.extend(dirty)
  72.             if not alive:
  73.                 self.active_particles -= 1
  74.                 dead_particles.append(particle)
  75.     
  76.         ## Adiciona as partículas mortas para a lista correta e retira-as da
  77.         ## lista de partículas ativas.
  78.         for particle in dead_particles:
  79.             self.dead_particles.append(particle)
  80.             self.particles.remove(particle)            
  81.     
  82.         ## Retorna se esse emissor está vivo ou não. Por padrão, emissores
  83.         ## estão vivos enquanto houver partículas ativas. Uma classe base
  84.         ## pode alterar isso, para criar emissores constantes, por exemplo.
  85.         return (self.active_particles > 0 and self.dead_particles), dirty_rects

Esse é o código básico para o emissor de partículas. Note que a função que cria as partículas está em branco, isso é porque a classe de emissor abstrata não sabe que tipo de partícula ela deve criar, então a essa função deve ser implementada pelos emissores específicos. Por exemplo, um emissor de “chuva” só iria criar partículas de gotas de água, e não de neve.

A próxima etapa é criar uma classe que controle as partículas. Novamente, vamos implementar apenas uma classe abstrata, afinal, estamos criando um sistema que deve ser flexível e funcionar para o máximo de efeitos que use partículas. Mas antes de pular direto para a programação, vamos revisar o que uma partícula precisa implementar.

  • A posição que a partícula ocupa no plano do jogo.
  • O tempo de vida da partícula.
  • Um método para atualizar o estado da partícula e desenhá-la na tela.
  • Um método para inicializar a partícula com novos valores, já que vamos reutilizar as partículas criadas.
  1. class cAbstractParticle:
  2.     def __init__(self, emitter, *args, **kwargs):
  3.         “”"
  4.         Inicializa a partícula.
  5.         ”"”
  6.         ## Uma referência para o emissor desta partícula.
  7.         self.emitter = emitter
  8.         
  9.         ## A posição desta partícula no plano do jogo.
  10.         self.x = kwargs.pop(“x”, 0)
  11.         self.y = kwargs.pop(“y”, 0)
  12.         
  13.         ## O tempo de vida restante desta partícula.
  14.         self.life = kwargs.pop(“life”, 0)
  15.         
  16.     def respawn(self):
  17.         “”"
  18.         Reinicializa a partícula com novos valores.
  19.         ”"”
  20.         pass
  21.         
  22.     def update(self, lag):
  23.         “”"
  24.         Atualiza a partícula na tela e retorna se ela está viva ou não.
  25.         ”"”
  26.         return True, []

Bem, com isso nosso sistema de partículas extensível está pronto. Podemos agora criar diversos tipos de emissores e partículas e encher a tela deles. Mas isso leva a um pequeno grande problema para qualquer desenvolvedor, principalmente se você escolheu Python como linguagem para desenvolvimento de jogos. Seria chato manter todos esses emissores e saber se eles estão vivos ou não. Queremos algo que seja simples, que permita adicionar emissores com uma chamada, e esquecer que eles existem lá. É essa praticidade que é importante em linguagens de script, e nós vamos implementar ela aqui também, usando uma classe para gerenciar os emissores.

O gerenciador de emissores é uma classe realmente básica. Tudo o que ela precisa é de métodos para adicionar e remover emissores facilmente, mas seria interessante se ela cuidasse de atualizar todos os emissores e destruí-los assim que morrerem, não é? Além disso, nós podemos agrupar os emissores por tipos e optimizar o cache de texturas em jogos que utilizam aceleração por hardware em OpenGL ou DirectX. Infelizmente isso não faz tanto sentido em PyGame puro, mas ainda podemos usar esse agrupamento para remover um grupo de emissores específico da tela.

Então, revisando o papel do gerenciador:

  • Adicionar e remover emissores.
  • Atualizar os emissores gerenciados e destruí-los quando morrerem.
  • Remover todos os emissores de um grupo específico.
  1. class cEmitterManager:
  2.     def __init__(self, *emitters, **kwargs):
  3.         “”"
  4.         Inicializa o gerenciador de emissores.
  5.         ”"”
  6.         self.emitters = {}
  7.         self.add(*emitters)
  8.     
  9.     def add(self, *emitters):
  10.         “”"
  11.         Adiciona novos emissores ao gerenciador.
  12.         ”"”
  13.         for emitter in emitters:
  14.             self.emitters.setdefault(emitter.id, [])
  15.             self.emitters[emitter.id].append(emitter)
  16.     
  17.     
  18.     def remove(self, *emitters):
  19.         “”"
  20.         Remove os emissores do gerenciador.
  21.         ”"”
  22.         for emitter in emitters:
  23.             if emitter in self.emitters.get(emitter.id, []):
  24.                 self.emitters[emitter.id].remove(emitter)
  25.     
  26.     
  27.     def remove_group(self, group):
  28.         “”"
  29.         Remove um grupo específico de emissores do gerenciador.
  30.         ”"”
  31.         if self.emitters.has_key(group):
  32.             del self.emitters[group]
  33.     
  34.     
  35.     def update(self, lag):
  36.         “”"
  37.         Atualiza os emissores do gerenciador e remove aqueles que morreram.
  38.         ”"”
  39.         ## Uma lista de emitters que morreram neste frame
  40.         dead_emitters = []
  41.         
  42.         ## Uma lista de áreas da tela que devem ser atualizadas
  43.         dirty_rects = []
  44.         
  45.         ## Percorre a lista de emitters e os atualiza
  46.         for id in self.emitters:
  47.             emitters = self.emitters[id]
  48.             for emitter in emitters:
  49.                 alive, dirty = emitter.update(lag)
  50.                 dirty_rects.extend(dirty)
  51.                 if not alive:
  52.                     dead_emitters.append(emitter)
  53.                     
  54.         ## Remove os emitters que morreram
  55.         self.remove(*dead_emitters)
  56.         
  57.         ## Retorna as áreas da tela que precisam ser atualizadas
  58.         return dirty_rects

- Conclusão -

Esse é um sistema básico, mas flexível para a criação de qualquer tipo de efeito utilizando partículas. Isso, claro, em um ambiente 2d. Algumas partes do código poderiam ser otimizadas, mas eu priorizei a simplicidade de leitura do código mais do que a velocidade. Ainda assim, esse código deve ser suficientemente rápido para uma boa parte dos efeitos gráficos. Vou deixar um pequeno pacote de exemplos do que pode ser feito com esse sistema de partículas. Os sources estão em Python e utilizam a PyGame pura.

Código fonte e exemplos de efeitos com partículas (~11 kb).

- Referências -

  • The Ocean Spray in Your Face - Jeff Lander (Game Developer, Julho de 1998)
  • Building an Advanced Particle System - John Van Der Burg (Gamasutra, Junho de 2000)
Comments
No Comments »
Categories
Python
Tags
efeitos especiais, partículas, PyGame
Comments rss Comments rss
Trackback Trackback

Bookmark this article! [?]

Del.icio.usDiggDiigoFacebookFurlGoogleWindows LiveRedditTechnoratiYahoo

PySiGaL @ Google code

Quildreen | September 11, 2009

So, I just commited the first version of PySiGal to Google Code. You guys can take a look at the project here:
http://code.google.com/p/pysigal/

There’s not much to it, yet.

  • Basic screen management using state machine.
  • Basic wrapper around pygame’s display to handle small updates on screen.
  • Basic sprite and sprite group class with z-ordering for drawing sprites in layers.
  • Basic image manipulation functions like scale, crop and tile, using caching algorithm to improve performance.
  • Basic packaging object.
  • Input wrapper around pygame events.

That’s all to it for now, but I’ll start working on some transitions and a SFont/Font manager module.

Comments
No Comments »
Categories
Libraries, Python
Tags
Game Development, PyGame, pysigal
Comments rss Comments rss
Trackback Trackback

Bookmark this article! [?]

Del.icio.usDiggDiigoFacebookFurlGoogleWindows LiveRedditTechnoratiYahoo

« Previous Entries

Languages

  • English (5)
  • Português (27)

.rss_feed//

.twitter//

    .calendário//

    February 2010
    M T W T F S S
    « Dec    
    1234567
    891011121314
    15161718192021
    22232425262728

    .categorias//

    • Arte (1)
      • 2d (1)
      • sketch/concept (1)
    • Contests (1)
    • Iniciantes (1)
    • Internet (1)
    • Jogos (9)
      • Casual (4)
      • PC (2)
      • Visual Novel (4)
    • Notícias (3)
    • Off-Topic (5)
    • Programação (18)
      • Delphi / Object Pascal (8)
      • Java/j2me (1)
      • Scripts (9)
        • JavaScript (2)
        • Python (8)
    • Resources (4)
      • Engines (1)
      • Frameworks (1)
      • Libraries (2)

    .tags//

    3D 12:12 demo Alpha Blending Animação Asphyre Sphinx AvalonRK Bitmask Casual Como fazer jogos Delphi Desenvolvimento de Jogos Engine Flash Framework Game Development Games Indústria de Jogos indie Jogos Media Player Meme MMSystem Object Pascal Off-Topic PC Processamento de imagem Programação Avançada Projetos puzzle PyGame Pyjamas pysigal Python pyweek RED -spider lily- RIA snapshot Sons Tetris Tile Mapping Trix twelfth honor deed Ubisoft visual novel Windows GDI

    WP Cumulus Flash tag cloud by Roy Tanck requires Flash Player 9 or better.

    .recente//

    • Oblivious —the darkness within—
    • O Estranho Mundo das Partículas
    • PySiGaL @ Google code
    • PySiGaL
    • RED -spider lily-!

    .blogroll//

    • DieSoft Games
    • Disk Chocolate
    • Eu quero diversão, caramba!
    • Ponto V
    • Rodrigo Flausino
    • Zizaco Development Log

    .desenvolvimento//

    • GameDev
    • GamedevBR
    • Programadores de Jogos
    • UniDev

    .pessoal//

    • AvalonRK
    • DeviantArt
    • LJ Pessoal
    • Nyah! Fanfiction
    • The Queen’s Dream

    .arquivo//

    • December 2009 (1)
    • November 2009 (1)
    • September 2009 (3)
    • August 2009 (4)
    • July 2009 (1)
    • June 2009 (3)
    • March 2009 (4)
    • November 2008 (1)
    • October 2008 (1)
    • September 2008 (1)
    • August 2008 (2)
    • July 2008 (3)
    • June 2008 (5)

    .licence//

    Creative Commons License
    Esta obra está licenciada sob uma Licença Creative Commons.
    rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox